wenmo8 发布的文章

抽象工厂模式(Abstract Factory Pattern)是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。



1

模式结构


UML 结构图:


图片


  • Factory(抽象工厂):声明一个用于创建抽象产品的接口

  • ConcreteFactory(具体工厂):用于创建具体的产品

  • Product(抽象产品):声明一个产品对象类型的接口

  • ConcreteProduct(具体产品):由具体工厂创建的具体产品



2

优缺点



优点:


  • 封装了产品的创建,使得不需要知道具体是哪种产品,只需要知道是哪个工厂即可。

  • 可以支持不同类型的产品,使得模式灵活性更强。

  • 可以非常方便的使用一族中的不同类型的产品。



缺点:


  • 结构过于臃肿,如果产品类型较多或产品族较多,会非常难于管理。

  • 每次如果添加一组产品,那么所有的工厂类都必须添加一个方法,这样违背了开放-封闭原则。所以一般适用于产品组合产品族变化不大的情况。



3

适用场景


  • 在不必指定产品的具体的情况下,创建多个产品族中的产品对象。



4

案例分析




BBA - 既造汽车,又造摩托车




图片


C++ 工厂方法模式 - BBA 各造各的车》要求产品为同一类型的(汽车),倘若要生产其他产品(例如:摩托车),则需要再创建对应的工厂。如果这样的话,后续工厂会越来越多,非常难以管理了


为了解决这个问题,我们可以扩大现有工厂的规模,增加一条生产线,专门用来生产摩托车,这样就不用新增工厂了,也便于后续的管理。


这就是抽象工厂模式,其根本思想就是将产品归类分组,然后将好几组产品构成一族。每个工厂负责生产一族产品,而工厂中的每个方法负责生产一种类型的产品。



5

代码实现



创建抽象产品


汽车和摩托车产品,分别由 ICar 和 IMotorcycle 表示:


// product.h
#ifndef PRODUCT_H
#define PRODUCT_H

#include <string>

using namespace std;

// 汽车接口
class ICar
{

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

// 摩托车接口
class IMotorcycle
{

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:
    std::string Name() override {
        return "Audi Car";
    }
};

/********** 摩托车 **********/
// 奔驰
class BenzMotorcycle : public IMotorcycle
{
public:
    string Name() override {
        return "Benz Motorcycle";
    }
};

// 宝马
class BmwMotorcycle : public IMotorcycle
{
public:
    string Name() override {
        return "Bmw Motorcycle";
    }
};

// 奥迪
class AudiMotorcycle : public IMotorcycle
{
public:
    string Name() override {
        return "Audi Motorcycle";
    }
};

#endif // CONCRETE_PRODUCT_H



创建抽象工厂


产品有了,当然要有相应的制造商与其相关联。所以呢,要有具体的工厂。但在这之前,需要一个抽象工厂:


// factory.h
#ifndef FACTORY_H
#define FACTORY_H

#include "product.h"

// 抽象工厂
class IFactory
{

public:
    virtual ICar* CreateCar() 0;  // 生产汽车
    virtual IMotorcycle* CreateMotorcycle() 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();
    }

    IMotorcycle* CreateMotorcycle() override {
        return new BenzMotorcycle();
    }
};

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

    IMotorcycle* CreateMotorcycle() override {
        return new BmwMotorcycle();
    }
};

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

    IMotorcycle* CreateMotorcycle() override {
        return new AudiMotorcycle();
    }
};

#endif // CONCRETE_FACTORY_H


这样一来,具体的产品就与其制造商关联起来了。



创建客户端


当一切准备就绪,就可以实现客户端了,利用相关产品的这种层次结构来创建产品。


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

using namespace std;

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

int main()
{
    // 奔驰
    IFactory *pBenzFactory = new BenzFactory();
    ICar *pBenzCar = pBenzFactory->CreateCar();
    IMotorcycle *pBenzMotorcycle = pBenzFactory->CreateMotorcycle();

    // 宝马
    IFactory *pBmwFactory = new BmwFactory();
    ICar *pBmwCar = pBmwFactory->CreateCar();
    IMotorcycle *pBmwMotorcycle = pBmwFactory->CreateMotorcycle();

    // 奥迪
    IFactory *pAudiFactory = new AudiFactory();
    ICar *pAudiCar = pAudiFactory->CreateCar();
    IMotorcycle *pAudiMotorcycle = pAudiFactory->CreateMotorcycle();

    cout << "Benz factory - Car: " << pBenzCar->Name() << endl;
    cout << "Benz factory - Motorcycle: " << pBenzMotorcycle->Name() << endl;

    cout << "Bmw factory - Car: " << pBmwCar->Name() << endl;
    cout << "Bmw factory - Motorcycle: " << pBmwMotorcycle->Name() << endl;

    cout << "Audi factory - Car: " << pAudiCar->Name() << endl;
    cout << "Audi factory - Motorcycle: " << pAudiMotorcycle->Name() << endl;

    SAFE_DELETE(pBenzCar);
    SAFE_DELETE(pBenzMotorcycle);
    SAFE_DELETE(pBenzFactory);

    SAFE_DELETE(pBmwCar);
    SAFE_DELETE(pBmwMotorcycle);
    SAFE_DELETE(pBmwFactory);

    SAFE_DELETE(pAudiCar);
    SAFE_DELETE(pAudiMotorcycle);
    SAFE_DELETE(pAudiFactory);

    getchar();

    return 0;
}


输出如下:

Benz factory - Car: Benz Car

Benz factory - Motorcycle: Benz Motorcycle

Bmw factory - Car: Bmw Car

Bmw factory - Motorcycle: Bmw Motorcycle

Audi factory - Car: Audi Car

Audi factory - Motorcycle: Audi Motorcycle


简单工厂模式(Simple Factory Pattern)又叫做静态工厂方法模式(Static Factory Method Pattern),属于创建型模式。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类的实例。


值得注意的是,简单工厂模式并不属于 23 种 GoF 设计模式之一。它是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。



1

模式结构


UML 结构图:


图片


  • 工厂(Factory):整个模式的核心,负责实现创建所有实例的内部逻辑。

  • 抽象产品(Product):所有具体产品的父类,负责描述所有实例所共有的公共接口。

  • 具体产品(ConcreteProduct):最终创建的具体产品



2

优缺点



优点:


  • 工厂类包含了必要的逻辑判断,根据指定的信息来创建对应的产品。客户端仅负责“消费”产品即可,实现了对象创建和使用的分离。

  • 客户端无需关心具体产品如何创建与组织,仅需知道具体产品所对应的参数即可,可以在一定程度减少使用者的记忆量。



缺点:


  • 由于工厂类集中了所有产品的创建逻辑(违反了高内聚责任分配原则),职责过重,一旦无法正常工作,整个系统都将受到影响。

  • 一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。


这些缺点在"工厂方法模式"中得到了一定的克服。



3

适用场景


  • 工厂类负责创建的对象比较少(不会造成工厂方法中的业务逻辑太过复杂)。

  • 客户端仅需知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。


注意:由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。



4

案例分析




BBA - 奔驰、宝马、奥迪




图片


互联网行业有三巨头 BAT,汽车界也有三大豪门 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 "concrete_product.h"

enum class CAR_TYPE {
    BENZ,  // 奔驰汽车
    BMW,   // 宝马汽车
    AUDI   // 奥迪汽车
};

// 工厂
class Factory
{

public:
    // 生产汽车
    ICar* CreateCar(CAR_TYPE type) {
        ICar *pCar = nullptr;
        switch(type) {
        case CAR_TYPE::BENZ:
            pCar = new BenzCar();  // 奔驰汽车
            break;
        case CAR_TYPE::BMW:
            pCar = new BmwCar();  // 宝马汽车
            break;
        case CAR_TYPE::AUDI:
            pCar = new AudiCar();  // 奥迪汽车
            break;
        default:
            break;
        }
        return pCar;
    }
};

#endif // FACTORY_H


这样以来,工厂就可以根据汽车类型来生产对应的汽车了。



创建客户端


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


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

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

int main()
{
    // 工厂
    Factory *pFactory = new Factory();

    // 生产奔驰、宝马、奥迪
    ICar *pBenzCar = pFactory->CreateCar(CAR_TYPE::BENZ);
    ICar* pBmwCar = pFactory->CreateCar(CAR_TYPE::BMW);
    ICar* pAudiCar = pFactory->CreateCar(CAR_TYPE::AUDI);

    cout << pBenzCar->Name() << endl;
    cout << pBmwCar->Name() << endl;
    cout << pAudiCar->Name() << endl;

    SAFE_DELETE(pBenzCar);
    SAFE_DELETE(pBmwCar);
    SAFE_DELETE(pAudiCar);
    SAFE_DELETE(pFactory);

    getchar();

    return 0;
}


输出如下:

Benz Car

Bmw Car

Audi Car


工厂方法模式(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++ 编写了整个现代计算机历史。

感谢你的阅读。