wenmo8 发布的文章

工厂方法模式(Factory Method Pattern)是一种常用的对象创建型设计模式,此模式的核心思想是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用以及方便后期维护拓展的目的。



1

模式结构


UML 结构图:


图片


  • Factory(抽象工厂):是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。

  • ConcreteFactory(具体工厂):实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且被应用程序调用以创建产品对象。

  • Product(抽象产品):所创建对象的基类,也就是具体产品的共同父类或共同拥有的接口。

  • ConcreteProduct(具体产品):实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。



2

优缺点



优点:


  • 克服了简单工厂模式违背开放-封闭原则的缺点,又保留了封装对象创建过程的优点,降低客户端和工厂的耦合性。所以说,“工厂方法模式”是“简单工厂模式”的进一步抽象和推广。



缺点:


  • 每增加一个产品,相应的也要增加一个子工厂,加大了额外的开发量。



3

适用场景


  • 对于某个产品,调用者清楚地知道应该使用哪个具体工厂服务,实例化该具体工厂,生产出具体的产品来。

  • 只是需要一种产品,而不想知道也不需要知道究竟是哪个工厂生产的,即最终选用哪个具体工厂的决定权在生产者一方,它们根据当前系统的情况来实例化一个具体的工厂返回给使用者,而这个决策过程对于使用者来说是透明的。



4

案例分析


工厂模式基本和简单工厂模式差不多,简单工厂模式 - 在产品子类添加时,需要添加一个判断分支,这违背了开放-封闭原则。因此,工厂模式就是为了解决这个问题而产生的。


图片


所以,对于《C++ 简单工厂模式 - BBA 造车了》一节中的 BBA 来说,要分别由对应的工厂来生产。



5

代码实现



创建抽象产品


汽车产品,由 ICar 表示:


// product.h
#ifndef PRODUCT_H
#define PRODUCT_H

#include <string>

using namespace std;

// 汽车接口
class ICar
{

public:
    virtual string Name() 0;  // 汽车名称
};

#endif // PRODUCT_H



创建具体产品


模型有了,就可以定义具体的汽车了,它们有不同的品牌:


// concrete_product.h
#ifndef CONCRETE_PRODUCT_H
#define CONCRETE_PRODUCT_H

#include "product.h"

// 奔驰汽车
class BenzCar : public ICar
{
public:
    string Name() override {
        return "Benz Car";
    }
};

// 宝马汽车
class BmwCar : public ICar
{
public:
    string Name() override {
        return "Bmw Car";
    }
};

// 奥迪汽车
class AudiCar : public ICar
{
public:
    string Name() override {
        return "Audi Car";
    }
};

#endif // CONCRETE_PRODUCT_H



创建抽象工厂


产品有了,当然要有相应的工厂来生产,但在这之前,需要一个抽象工厂:


// factory.h
#ifndef FACTORY_H
#define FACTORY_H

#include "product.h"

// 工厂接口
class IFactory
{

public:
    virtual ICar* CreateCar() 0;  // 生产汽车
};

#endif // FACTORY_H



创建具体工厂


为每个制造商创建对应的的工厂:


// concrete_factory.h
#ifndef CONCRETE_FACTORY_H
#define CONCRETE_FACTORY_H

#include "factory.h"
#include "concrete_product.h"

// 奔驰工厂
class BenzFactory : public IFactory
{
public:
    ICar* CreateCar() override {
        return new BenzCar();
    }
};

// 宝马工厂
class BmwFactory : public IFactory
{
public:
    ICar* CreateCar() override {
        return new BmwCar();
    }
};

// 奥迪工厂
class AudiFactory : public IFactory
{
public:
    ICar* CreateCar() override {
        return new AudiCar();
    }
};

#endif // CONCRETE_FACTORY_H


这样以来,每个工厂只负责生产自己的产品。



创建客户端


当一切准备就绪,就可以实现客户端了:


// main.cpp
#include "concrete_factory.h"
#include "product.h"
#include <iostream>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=nullptr;} }
#endif

int main()
{
    // 奔驰
    IFactory *pBenzFactory = new BenzFactory();
    ICar *pBenzCar = pBenzFactory->CreateCar();
    cout << "Benz Factory: " << pBenzCar->Name() << endl;

    // 宝马
    IFactory *pBmwFactory = new BmwFactory();
    ICar *pBmwCar = pBmwFactory->CreateCar();
    cout << "Bmw Factory: " << pBmwCar->Name() << endl;

    // 奥迪
    IFactory *pAudiFactory = new AudiFactory();
    ICar *pAudiCar = pAudiFactory->CreateCar();
    cout << "Audi Factory: " << pAudiCar->Name() << endl;

    SAFE_DELETE(pAudiCar);
    SAFE_DELETE(pAudiFactory);
    SAFE_DELETE(pBmwCar);
    SAFE_DELETE(pBmwFactory);
    SAFE_DELETE(pBenzCar);
    SAFE_DELETE(pBenzFactory);

    getchar();

    return 0;
}


输出如下:

Benz Factory: Benz Car

Bmw Factory: Bmw Car

Audi Factory: Audi Car



建造者模式(Builder Pattern),旨在将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。



1

模式结构


UML 结构图:


图片


  • Builder(抽象建造者):为创建一个产品对象的各个部件指定抽象接口。

  • ConcreteBuilder(具体建造者):实现 Builder 的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。

  • Director(指挥者):构造一个使用 Builder 接口的对象。

  • Product(产品):表示被构造的复杂对象。ConcreteBuilder 创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。



2

优缺点



优点:


  • 建造者独立,易于扩展

  • 便于控制细节风险



缺点:


  • 产品必须有共同点,范围有限制

  • 如果内部变化复杂,将会有很多的建造类



3

适用场景


  • 需要生成的对象具有复杂的内部结构

  • 需要生成的对象内部属性本身相互依赖



4

案例分析




Lenovo For Those Who Do - 联想



图片

联想旗下有众多系列的电脑,例如:ThinkPad、Yoga ...... 在科技创新的大潮下,产品层出不穷。电脑虽多,但结构基本一致,都是由 CPU、主板、内存、显卡等组成。如果为每台电脑都单独设计一个组装流程,管理的成本太高。这时,建造者模式就出现了,为所有系列指定一个统一的组装流程,所有的电脑都按照这个流程来组装。



5

代码实现



创建产品


这里的产品是 Computer,包含了 CPU、主板、内存、显卡等信息。


#ifndef PRODUCT_H
#define PRODUCT_H

#include <iostream>

using namespace std;

// 电脑
class Computer
{

public:
    void SetCpu(string cpu) { m_strCpu = cpu;}
    void SetMainboard(string mainboard) { m_strMainboard = mainboard; }
    void SetRam(string ram) { m_strRam = ram; }
    void SetVideoCard(string videoCard) { m_strVideoCard = videoCard; }

    string GetCPU() return m_strCpu; }
    string GetMainboard()  return m_strMainboard; }
    string GetRam() return m_strRam; }
    string GetVideoCard() return m_strVideoCard; }

private:
    string m_strCpu;  // CPU
    string m_strMainboard;  // 主板
    string m_strRam;  // 内存
    string m_strVideoCard;  // 显卡
};

#endif // PRODUCT_H



创建抽象建造者


电脑准备好之后,就可以定义组装流程了,由 Builder 来表示。


#ifndef BUILDER_H
#define BUILDER_H

#include "product.h"

// 建造者接口,组装流程
class IBuilder
{

public:
    virtual void BuildCpu() 0;  // 创建 CPU
    virtual void BuildMainboard() 0;  // 创建主板
    virtual void BuildRam() 0;  // 创建内存
    virtual void BuildVideoCard() 0;  // 创建显卡
    virtual Computer* GetResult() 0;  // 获取建造后的产品
};

#endif // BUILDER_H



创建具体建造者


接下来,就是让 ConcreteBuilder 就位。在这些组装者中,可以指定每台电脑要使用的具体部件。


#ifndef CONCRETE_BULIDER_H
#define CONCRETE_BULIDER_H

#include "builder.h"

// ThinkPad 系列
class ThinkPadBuilder : public IBuilder
{
public:
    ThinkPadBuilder() { m_pComputer = new Computer(); }
    void BuildCpu() override { m_pComputer->SetCpu("i5-6200U"); }
    void BuildMainboard() override { m_pComputer->SetMainboard("Intel DH57DD"); }
    void BuildRam() override  { m_pComputer->SetRam("DDR4"); }
    void BuildVideoCard() override { m_pComputer->SetVideoCard("NVIDIA Geforce 920MX"); }
    Computer* GetResult() override return m_pComputer; }

private:
    Computer *m_pComputer;
};

// Yoga 系列
class YogaBuilder : public IBuilder
{
public:
    YogaBuilder() { m_pComputer = new Computer(); }
    void BuildCpu() override  { m_pComputer->SetCpu("i7-7500U"); }
    void BuildMainboard() override { m_pComputer->SetMainboard("Intel DP55KG"); }
    void BuildRam() override { m_pComputer->SetRam("DDR5"); }
    void BuildVideoCard() override { m_pComputer->SetVideoCard("NVIDIA GeForce 940MX"); }
    Computer* GetResult() override return m_pComputer; }

private:
    Computer *m_pComputer;
};

#endif // CONCRETE_BULIDER_H



创建指挥者


联想要生产电脑了,直接委派给组装者,开干。


#ifndef DIRECTOR_H
#define DIRECTOR_H

#include "builder.h"

// 构造指挥官
class Direcror
{

public:
    void Create(IBuilder *builder) {
        builder->BuildCpu();
        builder->BuildMainboard();
        builder->BuildRam();
        builder->BuildVideoCard();
    }
};

#endif // DIRECTOR_H



创建客户端


现在,一起来看看客户端,创建任何产品都非常干净。


#include "concrete_bulider.h"
#include "director.h"
#include <string>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} }
#endif

int main()
{
    Direcror *pDirecror = new Direcror();
    ThinkPadBuilder *pTPBuilder = new ThinkPadBuilder();
    YogaBuilder *pYogaBuilder = new YogaBuilder();

    // 组装 ThinkPad、Yoga
    pDirecror->Create(pTPBuilder);
    pDirecror->Create(pYogaBuilder);

    // 获取组装后的电脑
    Computer *pThinkPadComputer = pTPBuilder->GetResult();
    Computer *pYogaComputer = pYogaBuilder->GetResult();

    // 测试输出
    cout << "-----ThinkPad-----" << endl;
    cout << "CPU: " << pThinkPadComputer->GetCPU() << endl;
    cout << "Mainboard: " << pThinkPadComputer->GetMainboard() << endl;
    cout << "Ram: " << pThinkPadComputer->GetRam() << endl;
    cout << "VideoCard: " << pThinkPadComputer->GetVideoCard() << endl;

    cout << "-----Yoga-----" << endl;
    cout << "CPU: " << pYogaComputer->GetCPU() << endl;
    cout << "Mainboard: " << pYogaComputer->GetMainboard() << endl;
    cout << "Ram: " << pYogaComputer->GetRam() << endl;
    cout << "VideoCard: " << pYogaComputer->GetVideoCard() << endl;

    SAFE_DELETE(pThinkPadComputer);
    SAFE_DELETE(pYogaComputer);
    SAFE_DELETE(pTPBuilder);
    SAFE_DELETE(pYogaBuilder);
    SAFE_DELETE(pDirecror);

    getchar();

    return 0;
}


输出如下:

-----ThinkPad-----

CPU: i5-6200U

Mainboard: Intel DH57DD

Ram: DDR4

VideoCard: NVIDIA Geforce 920MX

-----Yoga-----

CPU: i7-7500U

Mainboard: Intel DP55KG

Ram: DDR5

VideoCard: NVIDIA GeForce 940MX


数以百计的 C/C++ 替代品将会出现,但 C/C++ 将永远与我们同在!

每个 CPU 都带有一种称为 ISA(指令集架构)汇编的电路语言。ISA 程序集是一种硬件语言,由基本数据操作、数学计算和结构化编程(即 jmp)的操作组成。但是,为每个计算需求编写汇编代码无疑是耗时的,因此过去的程序员发明了对人类友好的语言和编译器。

计算机科学家先驱 Dennis Ritchie 为 Unix 操作系统的研发需求实现了 C 语言。在这段时期,整个计算机技术领域都在进行基础的最初建设,所以几乎所有的程序员都使用 C 语言来构建早期的计算程序,比如编译器、操作系统、数据库软件和网络程序。后来,C++ 扩展了 C 语言,保留了 C 语言的性能特点,一门具有更多开发人员特性的新的编程语言诞生了。

在 20 世纪 20 年代,程序员实现了 C/ C++ 的备选品,如 Go、D、Rust 和 Carbon,它们具有 C/ C++ 从未提供的各种特性。但这些语言仍然只是 C/ C++ 的备选品,而不是替代品,原因如下:

1C 和 C++ 是基础语言

如果我们追根溯源当今活跃在我们生命中的每一款计算机程序,总会发现它们诞生自 C 或 C++。想想你现在在做什么,你可能在谷歌 Chrome 上读到这篇文章,Chrome 开源浏览器 (Blink 渲染引擎、V8 和浏览器应用程序) 是用 C++ 写的。假如你在 GNU/Linux 上运行 Chrome,Linux 内核是用 C 写的。MySQL,最流行的关系数据库管理系统,是用 C/ C++ 写的。所有流行的操作系统都为内核函数提供了核心 C 或 C++ API。

即使存在稳定的 C/ C++ 替代方案,许多程序员仍然喜欢用 C 或 C++ 进行系统编程。在大多数情况下,程序员选择 C++ 是因为它是与操作系统级 API 通信的最佳语言。例如,谷歌用 C++ 编写了 Flutter 引擎:

图片

Flutter 引擎使用 C/ C++ 应用 GTK 库函数,

截图由作者提

数十年来,计算领域的大多数核心组件都使用 C/ C++ 作为实现语言,C/ C++ 语言也长期维持着语言语法的标准。打造 C/ C++ 的替代品就像在所有的建筑工程完工之后改变房子的地基。

2C 和 C++ 完全控制我们所写的内容

在编写源代码时,C/ C++ 可以自由地处理程序资源。例如,C/ C++ 允许你直接分配 / 释放用于存储数据元素的物理内存。C/ C++ 提供了一种使用本机操作系统级线程的方法,而不是像 Go 那样管理单独的并发运行时。

C/ C++ 没有提供自动内存管理 (垃圾回收) 特性,因此程序员应该谨慎有效地防止内存泄漏。看看 Meta 的 Folly 库源代码是如何实现手动内存管理策略的:

图片

Meta 的 Folly 库使用了手动内存管理功能

自动内存管理和内存处理的限制无疑是使语言变得现代、高效和更抽象的好方法,但是这些特性会在语言运行时产生性能开销,并降低程序员的自由度。

C 和 C++ 不限制内存访问,提供手动的内存管理操作,让程序员按照自己的意愿控制程序,从而把自由给了程序员。当你用 C/ C++ 编写程序时,你的源代码将有效地执行你指示的操作,就是这样。

3C 和 C++ 确实又快又高效

一个特定程序的效率取决于两个主要因素:程序员使用的算法的时间复杂度和二进制程序的效率。毫无疑问,我们可以控制算法的复杂性,因为我们可以通过更新源代码来改变它们。另一方面,二进制文件是编译器生成的,因此我们无法轻易从这方面提高效率。

但是,我们可以选择一个能够生成快速有效的二进制文件的编译器。GNU 编译器生成特定于平台的二进制文件,而不嵌入专用的运行时环境。C 编程执行模型使用 crt0 汇编指令段中定义的最小启动代码。看看下面的例子,Linux 上的 crt0 部分:

图片

在 C 语言中 main 函数之前执行的启动代码

C++ 无疑是一门复杂的语言,但它不像 Python 和 Golang 那样提供更高的抽象。此外,它还为你提供了一种使用首选标准语言版本 (即 C++ 14) 进行编译的方法。因此,自现代 C++ 特性使 C++ 开发复杂化以来,你可以只使用 C++ 中最小的特性。C++ 已经有 30 多年的历史了,并且从早期计算时代开始就对其性能进行了优化。

4C 和 C++ 是学术友好型语言

程序员编程通常始于职业生涯的不同阶段。一些程序员在他们上学的时候就使用第一台计算机设备学习编程。然而,大多数程序员都是在大学期间提高他们的编程技能的。幸运的是,几乎所有的大学都是为了让学生有机会学习计算机程序如何与硬件组件一起工作而开始教授了 C 语言编程。我写了以下文章来进一步解释计算机程序是如何与硬件连接的:

编写优化代码前需要知道的 5 件事

https://www.infoq.cn/link?target=https%3A%2F%2Flevelup.gitconnected.com%2F5-things-to-know-before-you-write-optimized-code-3ca424110db1

这些事实有助于您以优化的性能给硬件和程序员留下深刻印象

https://www.infoq.cn/link?target=https%3A%2F%2Flevelup.gitconnected.com%2F5-things-to-know-before-you-write-optimized-code-3ca424110db1

后来,大多数大学教授 C++ 的数据结构和算法基础知识,而不使用 C++ 的复杂部分。大学生通常在学习了与算法相关的课程后,就会进入竞争激烈的编程领域。大多数有竞争力的程序员都喜欢 C++,因为它速度快,内置的最优数据结构可用性高,语法少。

Rust 无疑是一种很好的语言,具有内存安全、高性能和内置特性,但是 Rust 语法对于第一次编写代码的开发人员并不友好。对于工业用途来说,如果你的团队希望获得类似 C 语言的最小的高效代码和类似 python 的开发环境,Go 是一种很好的语言。但是,对于学术用途来说,Go 的抽象太过简单,并且不能与传统的伪代码保持一致,所以学术讲师永远不会用 Go 来替代 C/ C++。

下面的文章解释了为什么每个程序员都应该用 C 语言开始编程:

为什么每个开发人员都应该使用 C 语言开始编程

https://www.infoq.cn/link?target=https%3A%2F%2Fshalithasuranga.medium.com%2Fwhy-every-developer-should-start-programming-with-c-39b3a87392bf

你可以从任何一种语言开始编程——但是从 C 开始有更多的好处!

https://www.infoq.cn/link?target=https%3A%2F%2Fshalithasuranga.medium.com%2Fwhy-every-developer-should-start-programming-with-c-39b3a87392bf

5现代替代方案仍然需要 C,它们专注于不同的目标

如前所述,所有 POSIX 操作系统和非 POSIX 操作系统 (即 Windows) 都提供了一个 C 库来处理内核操作,因此从 C/ C++ 调用内核特性很容易,因为我们不需要编写特定于语言的绑定或第三方包装器。一些操作系统甚至预先包含 GNU C/ C++ 编译器和调试器来促进 C/ C++ 的开发。如果我们使用 Rust 和 Go,需要特定于语言的第三方绑定来与操作系统 API 通信。现代替代语言仍然提供了调用 C 代码的方法。例如,Go 提供 Cgo 特性来调用 C 代码。

几乎所有的 C/ C++ 替代方案都力求用与 C++ 截然不同的语言语法来提供缺少的 C++ 特性。

如果程序员在寻找 C/ C++ 的替代方案,他们通常会期望一个平稳且耗时较少的迁移过程。

此外,他们也不期望学习一门新语言来为他们不喜欢的 C/ C++ 特性找到解决方案。

程序员们针对低层次编程用例设计和改进了 C 语言,没有一种现代语言是完全针对 C 语言的目标而创建的。C++ 使 C 语言更具有未来感,并自低级编程阶段进行了提升。Rust、Go、D 和 Carbon 都是 C/ C++ 的备选品——而不是替代品,这些备选品都有自己的未来目标。

下面的文章解释了为什么每个程序员在他们的职业生涯中都需要学习 C++ 语言:

为什么每个程序员都应该在他们的职业生涯中学习 C++

https://www.infoq.cn/link?target=https%3A%2F%2Flevelup.gitconnected.com%2Fwhy-every-programmer-should-learn-c-during-their-careers-959e1bc2ea68

掌握 C++ 并不是一件容易的事,但是一旦你做到了,你将获得无价收益

https://www.infoq.cn/link?target=https%3A%2F%2Flevelup.gitconnected.com%2Fwhy-every-programmer-should-learn-c-during-their-careers-959e1bc2ea68

6结语

以前的程序员在 C/ C++ 进化时期书写了我们的计算机历史。他们用 C 和 C++ 构建操作系统内核、编程语言、数据库系统、移动操作系统和网络软件。多亏了 C/ C++,现在几乎所有的现代 Web 服务都能工作。例如,最流行的 Web 服务器软件,如 Apache HTTP 和 Nginx,就是使用 C/ C++ 作为实现语言。

以前的程序员几乎用 C/ C++ 编写了所有流行的内部网络浏览器和网络软件组件。Web 开发人员喜欢选择 Java 和 Node.js 用于 Web 服务,但 Java 和 Node.js 都是因 C/ C++ 而来的。

一些程序员认为像 Rust、Go、D 和 Carbon 这样的流行语言可以替代 C/ C++。同时,一些程序员考虑使用这些语言作为 C/ C++ 的备选品,认为它们在未来可能取代 C/ C++。这些现代语言是为特定的目的和需求而设计的——而不是取代 C/ C++。

微软创建了 TypeScript,但我们仍然使用 JavaScript。Jetbrains 创造了 Kotlin,但我们仍然使用 Java。数以百计的 C/ C++ 备选品出现了,但程序员仍将使用 C/ C++,因为重写面向 C/ C++ 的计算机历史是不现实的。

这并不意味着 C 和 C++ 是最好的语言——在某些方面 (例如复杂性、内存安全性等),备选语言可能比 C/ C++ 更好,但它们无法进入 C/ C++ 的领域,因为以前的程序员用 C 和 C++ 编写了整个现代计算机历史。

感谢你的阅读。


应用程序换到另一台机器运行时报错:“Ora-01017:用户名/口令无效; 登录被拒绝。”

检查各配置文件,确认用户名与密码无误,且通过plsql developer等工具都可正常连接,说明另有原因。

后经排查,发现新运行机器安装的是oracle 12.1c客户端,而服务器是12.2c,正是这引起了Ora-01017错误。

解决方法:

1) 在服务器环境,修改sqlnet.ora,添加以下两行:

SQLNET.ALLOWED_LOGON_VERSION_SERVER=10
SQLNET.ALLOWED_LOGON_VERSION_SERVER=10

其中的数字代表了可允许连接的最小版本号。

2) 重启监听服务:lsnrctl restart

3) 重新设置一遍用户密码:

SQL> conn / as sysdba;
SQL>ALTER USER myuser IDENTIFIED BY mypassword;


MySQL中字段类型为 longtext 的字段值保存的是Blob (Binary large objects),所以在导出sql或者将sql查询导出为其他格式的数据时,需要提前将字段类型转换一下,转换方式:

  使用MySQL的CAST()函数或者CONVERT()函数。

语法: CAST(value as type);
       CONVERT(value, type);

示例:

SELECT
	CAST(t.longtextField AS CHAR) AS longtextField
FROM
	tableName t;
SELECT
	CONVERT(t.longtextField, CHAR) AS longtextField
FROM
	tableName t;