鼠键不离,码不停蹄.|

Leaos

园龄:4年10个月粉丝:33关注:1

设计模式之简单工厂模式

概述

简单工厂模式其实并不属于 GoF 23 个经典设计模式,但是它是其他工厂模式的基础,其基本流程如下:

首先将需要创建的各种不同对象的相关代码封装到不同的类中,这些类称为具体产品类,每一个具体产品类都是抽象产品类的子类;然后提供一个工厂类用于创建各种产品,在工厂类中提供一个创建产品的工厂方法,该方法可以根据所传入的参数不同创建不同的具体产品对象;客户端只需调用工厂类的工厂方法并传入相应的参数即可得到一个产品对象。

简单工厂模式的定义如下:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,因此简单工厂模式又被称为静态工厂方法,它属于类创建型模式。

其结构图如图所示:

从图中可知,简单工厂模式包含如下三个角色:

  1. Factory(工厂角色):即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法 factoryMethod(),其返回类型为抽象产品类型 Product。
  2. Product(抽象产品角色):它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,似的在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
  3. ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的而某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。

典型的抽象产品类代码如下:

class Product {
public:
virtual void methodSame(); // 公共业务方法
virtual void methodDiff() = 0; // 抽象业务方法
};

典型的具体产品类代码如下:

class ConcerteProduct {
public:
virtual void methodDiff() override;
};

简单工厂的核心是工厂类,在没有工厂类之前,客户端一般会使用 new 关键字来直接创建产品对象,而在引入工厂类之后,客户端可以通过工厂类来创建产品,在简单工厂模式中,工厂类提供了一个静态工厂方法供客户端使用,根据所传入的参数不同可以创建不同的产品对象。典型的工厂类代码如下:

class Factory {
public:
static Product* getProduct(string arg) {
Product* product = nullptr;
if (arg == "a" || arg == "A") {
product = new ConcreteProductA();
// initalization
} else if (arg == "b" || arg == "B") {
prodcut = new ConcreteProductB();
// initalization
}
return product;
}
};

实践模拟

现在我们使用简单工厂模式来构建一个图表类,用于绘制各种统计图样式,例如折线图、柱状图、饼图等。其图标结构如下:

如图中所示,Chart 充当抽象产品类,其子类 HistogramChart、PieChart 和 LineChart 充当具体产品类,ChartFactory 充当工厂类。完整代码如下:

class Chart {
public:
virtual void display() = 0;
};
class HistogramChart : public Chart {
public:
HistogramChart() {
cout << "创建柱状图!" << endl;
}
virtual void display() override {
cout << "显示柱状图" << endl;
}
};
class LineChart : public Chart {
public:
LinemChart() {
cout << "创建折线图!" << endl;
}
virtual void display() override {
cout << "显示折线图" << endl;
}
};
class PieChart : public Chart {
public:
PieChart() {
cout << "创建饼状图!" << endl;
}
virtual void display() override {
cout << "显示饼状图" << endl;
}
};
class ChartFactory {
public:
static Chart* getChart(string type) {
Chart* chart = nullptr;
if (type == "histogram") {
chart = new HistogramChart();
// initialization
} else if (type == "pie") {
chart = new PieChart();
// initialization
} else if (type == "line") {
chart = new LineChart();
// initialization
}
return chart;
}
};

方案的改进

如果开发人员在创建具体的 Chart 对象时,每更换一个 Chart 对象都需要修改客户端代码中静态工厂方法的参数,客户端代码将要重新编译,这对于客户端而言,违反了开闭原则。

而如下的方法则可以在不修改客户端代码的前提下更换具体产品对象。其方法是将静态工厂方法的参数存储在 XMLproperties 格式的文件中,如 config.xml,代码如下;

<?xml version="1.0"?>
<config>
<chartType>histogram</chartType>
</config>

此后,如果需要更换具体图标对象,只需修改配置文件 config.xml 而无需修改任何源代码,符合开闭原则。

有时候,为了简化工厂模式,可以将抽象产品类和工厂类合并,将静态工厂方法移至抽象产品类中,如图所示:

在图中,客户端可以通过产品父类的静态工厂方法,根据参数的不同创建不同类型的产品子类对象。

总结

所有的工厂模式都强调一点:两个类 A 和 B 之间的关系应该仅仅是 A 创建 B 或是 A 使用 B,而不能两种关系都有。将对象的创建和使用分离,也使得系统更加符合单一职责原则,有利于对功能的复用和系统的维护。

此外,将对象的创建和使用分离还有一个好处:防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中。因为有时候创建一个对象不只是简单调用其构造函数,还需要设置一些参数,可能还需要配置环境,如果将这些代码散落在每一个创建对象的客户类中,势必会出现代码重复、创建蔓延的问题,而这些客户类其实无须承担对象的创建工作,只需使用已创建好的对象就可以了。此时,可以引入工厂类来封装对象的创建逻辑和客户代码的实例化/配置选项。

优点

  1. 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅 "消费" 产品。简单工厂模式实现了对象创建和使用的分离。
  2. 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
  3. 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点

  1. 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
  2. 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
  3. 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
  4. 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

适用场景

  1. 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  2. 客户端只知道传人工厂类的参数,对于如何创建对象并不关心。

所有代码见 Kohirus-Github

本文作者:Leaos

本文链接:https://www.cnblogs.com/tuilk/p/16813247.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Leaos  阅读(46)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起