简单工厂模式

简单工厂模式

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

简单工厂模式有如下几个角色:
● Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Factory {
//静态工厂方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalsIgnoreCase("A")) {
product = new ConcreteProductA();
//初始化设置product
}
else if (arg.equalsIgnoreCase("B")) {
product = new ConcreteProductB();
//初始化设置product
}
return product;
}
}

● Product(抽象产品角色):它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。

1
2
3
4
5
6
7
8
abstract class Product {
//所有产品类的公共业务方法
public void methodSame() {
//公共方法的实现
}
//声明抽象业务方法
public abstract void methodDiff();
}

● ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。

1
2
3
4
5
6
class ConcreteProduct extends Product {
//实现业务方法
public void methodDiff() {
//业务方法的实现
}
}

相关案例实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
具体实现一:
interface Chart{
public void display();
}
public class pie implments Chart{
public void display(){
System.out.println("display with pie")
}
}
public class histogram implements Chart{
public void display(){
System.out.println("display with histogram")
}
}
class ChartFactory{
public static Chart getChart(String type){
Chart chart = null;
if(type.equals("pie")){
chart = new pie();
}else if(type.equals("histogram")){
chart = new histogram();
}else{
}
return chart;
}
}

引入配置文件和工具类XMLUtil之后,客户端代码修改如下:

1
2
3
4
5
6
7
8
class Client {
public static void main(String args[]) {
Chart chart;
String type = XMLUtil.getChartType(); //读取配置文件中的参数
chart = ChartFactory.getChart(type); //创建产品对象
chart.display();
}
}

虽然简单工厂模式实现了对象的创建和使用分离,但是仍然存在如下两个问题:
(1) 工厂类过于庞大,包含了大量的if…else…代码,导致维护和测试难度增大;

(2) 系统扩展不灵活,如果增加新类型的日志记录器,必须修改静态工厂方法的业务逻辑,违反了“开闭原则”。

工厂方法模式

定义一个用于创建对象(抽象工厂)的接口,让子类决定将哪一个类实例化。
工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。

在工厂方法模式结构图中包含如下几个角色:
● Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。
● ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
● Factory(抽象工厂):在抽象工厂类中,声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。

1
2
3
interface Factory{
public Product factoryMethod();
}

● ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

1
2
3
4
5
class ConcreteFactory implements Factory{
public Product factoryMethod() {
return new ConcreteProduct();
}
}

在实际使用时,具体工厂类在实现工厂方法时除了创建具体产品对象之外,还可以负责产品对象的初始化工作以及一些资源和环境配置工作,例如连接数据库、创建文件等
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。

ps:面向对象编程补充

继承使用的三大要点:
1.父类只给子类提供服务,并不涉及子类的业务逻辑
Java中Object并不影响Model View Controller的执行逻辑和业务,Object为子类提供基础服务,例如内部计数等

2.层次关系明显,功能划分清晰,父类和子类各做各的。
Object并不参与MVC的管理中,那些都只是各自派生类自己要处理的事情。

3.父类的所有变化,都需要在子类中体现,也就是说此时耦合已经成为了需求。

结论:万不得已不要用继承,优先考虑组合。

多态:多态一般都要和继承结合起来,其本质是子类通过覆盖父类的方法,来使得同一对象同一方法的调用产生不同的结果。

使用多态的两个要素:
1. 如果引入多态之后导致对象角色不够单纯,那就不应该引入多态;如果引入多态之后依然是单纯角色,则可以使用多态。
2. 如果要覆重的方法是角色业务的其中一个组成部分,那么就最好不要用多态的方案,用IOP,因为在外界调用的时候其实并不需要通过多态来满足定制化的需求。
多态在面向对象程序中的应用相当广泛,只要有继承的地方,或多或少都会用到多态。然而多态比起继承来,更容易被不明不白地使用,一切看起来都那么顺其自然。在客户程序员这边,一般是只要多态是可行方案的一种,到最后大部分都会采用多态的方案来解决问题。
然而多态正如它名字中所暗示的,它有非常大的潜在可能引入不属于对象初衷的逻辑,巨大的灵活性也导致客户程序员在面对问题的时候不太愿意采用其他相对更优的方案,比如IOP。在决定是否采用多态时,我们要有一个清晰的角色概念,做好角色细分,不要角色混乱。该是拦截器的,就给他制定一个拦截器接口,由另一个对象(逻辑上的另一个对象,当然也可以是自己)去实现接口里的方法集。不要让一个对象在逻辑上既是拦截器又是业务模块。这样才方便未来的维护。另外也要注意被覆重方法的作用,如果只是单纯为了提供父类所需要的中间数据的,一律都用IOP,这是比直接采用多态更优的方案。