一. 工厂方法模式

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类

我们模拟一下咖啡机生产咖啡这个例子,咖啡有很多种类,如拿铁和摩卡

#include <cstdio>                                                                                                                                       
#include <string>
#include <memory>

class Coffee{
protected:
    std::string type;

protected:
    Coffee(const std::string &type)
    :type(type){}

public:
    virtual ~Coffee() = default;

public:
    virtual void getTaste(void) const = 0;
};

class LatteCoffee : public Coffee{
public:
    LatteCoffee()
    :Coffee("Latte"){}

public:
    void getTaste(void) const override{
        printf("%s, gentle and mellow\n", this->type.c_str());
        return;
    }
};

class MochaCoffee : public Coffee{
public:
    MochaCoffee()
    :Coffee("Mocha"){}

public:
    void getTaste(void) const override{
        printf("%s, silky and mellow\n", this->type.c_str());
        return;
    }
};

class CoffeeMaker{
public:
    virtual ~CoffeeMaker() = default;

public:
    virtual std::unique_ptr<Coffee> makeCoffee(void) const = 0;
};

class LatteMaker : public CoffeeMaker{
public:
    std::unique_ptr<Coffee> makeCoffee(void) const override{
        return std::make_unique<LatteCoffee>();
    }
};

class MochaMaker : public CoffeeMaker{
public:
    std::unique_ptr<Coffee> makeCoffee(void) const override{
        return std::make_unique<MochaCoffee>();
    }
};

int main(void){
    std::unique_ptr<CoffeeMaker> latte_maker = std::make_unique<LatteMaker>();
    latte_maker->makeCoffee()->getTaste();

    std::unique_ptr<CoffeeMaker> mocha_maker = std::make_unique<MochaMaker>();
    mocha_maker->makeCoffee()->getTaste();
    return 0;
}

工厂方法模式克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的的优点

然而工厂方法模式并不适用于有多种分类的产品,或具有二级分类的产品

  1. 多种分类:如电子白板程序,可以绘制各种图形,那么画笔的绘制功能可以理解为一个工厂,而图形可以理解为一种产品;图形可以根据形状分为直线、矩形、椭圆等,也可以根据颜色分为红色图形、绿色图形、蓝色图形等。
  2. 二级分类:如家电工厂,它可能同时生产冰箱、空调和洗衣机,那么冰箱、空调、洗衣机就属于一级分类;而洗衣机又可分为高效型和节能型,高效型洗衣机和节能型洗衣机就属于二级分类

为了解决这类问题,引入抽象工厂模式

二. 策略模式

策略模式:它定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户

我们来模拟一个例子,商场收银软件,营业员需要根据客户所购买商品的单价和数量,向客户收费;收费方式分为:正常收费、满 300 返 100、打 8 折

#include <cstdio>                                                                                                                                       
#include <string>
#include <memory>

class Charge{
public:
    virtual ~Charge() = default;

public:
    virtual double charge(double money) const = 0;
};

class Normal : public Charge{
public:
    double charge(double money) const override{
        return money;
    }
};

class Rebate : public Charge{
public:
    double charge(double money) const override{
        return money * 0.8;
    }
};

class Return : public Charge{
public:
    double charge(double money) const override{
        int result = money;
        if(result >= 300)
            result -= 100;
        return result;
    }
};

class ChargeSoft{
private:
    std::unique_ptr<Charge> charge_type;

public:
    ChargeSoft(const std::string &charge_type){
        if(charge_type == "normal")
            this->charge_type = std::make_unique<Normal>();
        else if(charge_type == "rebate")
            this->charge_type = std::make_unique<Rebate>();
        else if(charge_type == "return")
            this->charge_type = std::make_unique<Return>();
        else
            this->charge_type = nullptr;
    }

public:
    double charge(double price, int number) const{
        return this->charge_type->charge(price * number);
    }
};

int main(void){
    ChargeSoft charge_soft("return");
    printf("Money: %lf\n", charge_soft.charge(30, 10));
    return 0;
}

策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

作业

生命游戏 ,简称为生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

  1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
  2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
  3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
  4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;

下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

示例 1: 输入:board = ((0,1,0), (0,0,1), (1,1,1), (0,0,0))

输出:((0,0,0), (1,0,1), (0,1,1), (0,1,0))

示例 2: 输入:board = ((1,1), (1,0))

输出:((1,1), (1,1))