工厂模式是一种创建型模式。一般来说,根据创建产品的复杂程度和抽象程度,我们可以将工厂模式分为简单工厂模式,工厂方法模式和抽象工厂模式。
简单工厂模式
仅仅对创建产品的过程做了一个浅层的封装,把创建产品的逻辑规则封装在了一个工厂类中,根据不同的需求来产生对应的产品。
假设我们要生产书籍,先定义一个书的接口
public interface Book {
void make();
}
有Java
书籍
public class JavaBook implements Book {
@Override
public void make() {
System.out.println("making java book...");
}
}
有Python
书籍
public class PythonBook implements Book {
@Override
public void make() {
System.out.println("making python book...");
}
}
创建书籍的工厂
public class BookFactory {
public static Book creat(Class clazz) {
if (clazz != null) {
try {
return (Book) clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
当我们需要书籍的时候
# 生产Java书
Book book = BookFactory.creat(JavaBook.class);
book.make();
# 生产Python书
Book book = BookFactory.creat(PythonBook.class);
book.make();
UML 图如下
这样来看一个简单工厂能根据不同的需求正常生产各种想要的书籍,看起来一切正常。但是突然某一天,工厂接到了新的需求,Java的书籍需要增加精装版,Python书籍需要更改规格,这个时候是不是得在工厂类里增加非常讨厌的if-else
了,是不是有种把创建对象的逻辑换个地方了的感觉😂😂
假设到了某一天,这个工厂业务线变大了,想要生产其他产品了,这个时候会发现现有的工厂类并没有办法满足我们的需求,这个就是简单工厂模式的局限性了。
简单总结一下:
-
简单工厂模式可以避免创建对象过程中的逻辑规则的暴露,只需传入参数即可创建对象,但是比较适合创建对象数量较少、规则比较稳定的业务
-
工厂类职责相对过重,增加新的产品需要修改工厂类的创建逻辑,违背了开背原则
-
不易于拓展相对复杂的产品结构
工厂方法模式
工厂方法模式是指定义一个创建对象的接口,由实现这个接口的类来决定对哪个类进行实例化,也就是说工厂方法模式将实例化的过程推迟到实现创建对象接口的子类中进行。
是不是有点绕,通俗来讲就是在简单工厂模式的时候,所有东西都在一个工厂里完成,啥都混在一起生成创建。现在工厂方法模式之后就相当于开分厂,各个工厂各司其职只生产自己的东西,即使有改动也只是在自己对应的工厂里修改,不干扰其他工厂。先看下代码的实现:
定义一个总的工厂,这个工厂类似现在的企业总部,负责决策,没有厂房,不生产产品
public interface BookFactory {
/**
* 创建对象接口的创建对象的方法,此工厂不负责产品的创建,而是把创建对象的职责给了实现此接口的子类
* @return
*/
Book create();
}
负责实际生产产品的子类,相当于企业总部开在各个地方的工厂厂房,里面有生产线
负责Java
书籍的工厂
public class JavaBookFactory implements BookFactory{
@Override
public Book create() {
System.out.println("印刷Java书籍时的其他要求");
System.out.println("没有对象New一个就有了");
return new JavaBook();
}
}
负责Python
书籍的工厂
public class PythonBookFactory implements BookFactory {
@Override
public Book create() {
System.out.println("印刷Python书籍时的需求");
System.out.println("我能大数据,我能AI,我无所不能");
return new PythonBook();
}
}
看下它们之间的UML图
通过类图结合工厂方法模式的定义,工厂方法就是我们在BookFactory
接口中定义了一个创建对象的方法create
,但是这个方法并不创建真正的对象,而是将对象的实例化推迟到了实现这个接口的子类中来,也就是在子类中真正的创建产品。相比简单工厂来说做了解耦。
现在生产书变成这样了
# 印刷Java书籍
BookFactory javaFactory = new JavaBookFactory();
Book javaBook = javaFactory.create();
javaBook.make();
# 看下输出了什么
印刷Java书籍时的其他要求
没有对象New一个就好了
making java book...
# 印刷Python书籍
BookFactory pythonFactory = new PythonBookFactory();
Book pythonBook = pythonFactory.create();
pythonBook.make();
# 看下输出了什么
印刷Python书籍时的需求
我能大数据,我能AI,我无所不能
making python book...
就像上面说的,每个工厂各司其职,互不干扰,当有工厂需要改动需求的时候更改自己内部的实现就可以,当有新需求的时候只需要创建新的工厂,也更好的满足程序设计的开闭原则。
你可能就会有疑问了,如果一家工厂不止于生产书籍了,能生产书籍,那么我也要生产海报,生产报纸。同理,其他工厂也可以具备这个能力来生产书籍、报纸和海报,工厂方法模式没有办法满足我们新的需求,这个时候需要抽象工厂模式了。
抽象工厂模式
抽象工厂模式提供一个接口,用于创建或者依赖对象的家族,而不需要明确指定具体类。我们把一个工厂能生产的书籍、报纸、海报这些叫做一个产品族,抽象工厂模式可以用来创建产品的家族,生产多个相关的产品。
抽象工厂允许用户使用抽象的接口来创建一组相关的产品,而不需知道实际产出的产品具体是什么。
现在,我们引入出版社。出版社需要发布书籍、海报和报纸,现在有Alpha和Beta两个工厂能提供印刷,每个工厂都可以基于特定需求进行定制印刷品。比如Alpha工厂的印刷包装年轻化,而Beta工厂的偏向于精装走高端收藏风格。来看下代码
工厂现在需要生产书籍、海报和报纸,定义一个工厂接口和两个工厂的实现类
public interface BookFactory {
/**
* 生产书籍
* @return
*/
Book createBook();
/**
* 生产报纸
* @return
*/
Newspaper createNewspaper();
/**
* 生产海报
* @return
*/
Poster createPoster();
}
public interface Newspaper {
}
public interface Poster {
}
public class AlphaFactory implements BookFactory {
@Override
public Book createBook() {
System.out.println("AlphaFactory暂时不生产书籍");
return null;
}
@Override
public Newspaper createNewspaper() {
System.out.println("AlphaFactory生产的报纸");
return new AlphaNewspaper();
}
@Override
public Poster createPoster() {
System.out.println("AlphaFactory生产的简单风格的海报");
return new AlphaPoster();
}
}
public class BetaFactory implements BookFactory {
@Override
public Book createBook() {
System.out.println("BetaFactory生产的Python书籍");
return new PythonBook();
}
@Override
public Newspaper createNewspaper() {
System.out.println("BetaFactory生产的报纸");
return new BetaNewspaper();
}
@Override
public Poster createPoster() {
System.out.println("BetaFactory生产的精装海报");
return new BetaPoster();
}
}
再定义一个出版社抽象类
public abstract class Publisher {
Book book;
Poster poster;
Newspaper newspaper;
/**
* 制造印刷
*/
abstract void produce();
}
然后真正的出版社来继承这个抽象类
public class AlphaPublisher extends Publisher {
private final BookFactory bookFactory;
public AlphaPublisher(BookFactory bookFactory) {
this.bookFactory = bookFactory;
}
@Override
void produce() {
System.out.println("这是alpha出版社要的产品");
book = bookFactory.createBook();
poster = bookFactory.createPoster();
newspaper = bookFactory.createNewspaper();
}
}
这是出版社和工厂通过组合的方式有了关系,传入不同的工厂,出版社就可以得到对应工厂制造的一组产品,可以写一个测试类来看下
public class AbstractFactoryClient {
public static void main(String[] args) {
BookFactory alphaFactory = new AlphaFactory();
Publisher publisher = new AlphaPublisher(alphaFactory);
publisher.produce();
}
}
// 输出
这是alpha出版社要的产品
AlphaFactory暂时不生产书籍
AlphaFactory生产的简单风格的海报
AlphaFactory生产的报纸
在我们创造产品的时候,只需要一个对应工厂就可以生产产品,并不需要知道这个工厂具体实现了什么。与此同时,我们也可以有其他的工厂,其他的出版社,这种拓展更好的满足了开闭原则。
看起来工厂方法是不是潜伏在抽象工厂里面?没错,抽象工厂很多时候经常以工厂方法的方式实现。抽象工厂的任务是定义一个负责创建一组产品的接口,这个接口内每个方法负责创建一个具体的产品,同时我们利用抽象工厂的子类来提供这些具体的做法。所以,在抽象工厂中使用工厂方法是相当自然的做法。
小结
通过工厂模式学会一个新的设计原则,依赖倒置原则——高层模块不应该依赖底层模块,二者都应该依赖其抽象,不要依赖具体类。抽象不应该依赖细节;细节应该依赖抽象,尽可能的让事情保持抽象,降低系统的耦合性。
-
简单工厂虽然不是一个真正的设计模式,但是确实一个简单的将客户端程序解耦的方法
-
工厂方法将对象的创建委托到子类,子类实现工厂方法来创建对象
-
抽象工厂使用对象组合,对象的创建被实现在抽象工厂所暴露出来的方法里,也就是利用一个抽象接口创建一组对象