在设计原则中有这样一句话“我们应该针对接口编程,而不是正对实现编程”。但是我们还是在一直使用new关键字来创建一个对象,这不就是在针对实现编程么?
针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。如果代码是针对接口而写,那么可以通过多态,它可以与任何新类实现该接口。但是,当代码使用一大堆的具体类时,等于是自找麻烦,因为一旦加入新的具体类,就必须要改变代码。在这里我们希望能够调用一个简单的方法,我传递一个参数过去,就可以返回给我一个相应的具体对象,这个时候我们就可以使用简单工厂模式。
# 引入
1)还没有工厂时代:假如还没有工业革命,如果一个客户要一款宝马车,一般的做法是客户去创建一款宝马车,然后拿来用。
2)简单工厂模式:后来出现工业革命。用户不用去创建宝马车。因为客户有一个工厂来帮他创建宝马.想要什么车,这个工厂就可以建。比如想要320i系列车。工厂就创建这个系列的车。即工厂可以创建产品。
3)工厂方法模式时代:为了满足客户,宝马车系列越来越多,如320i,523i,30li等系列一个工厂无法创建所有的宝马系列。于是由单独分出来多个具体的工厂。每个具体工厂创建一种系列。即具体工厂类只能创建一个具体产品。但是宝马工厂还是个抽象。你需要指定某个具体的工厂才能生产车出来。
4)抽象工厂模式时代:随着客户的要求越来越高,宝马车必须配置空调。于是这个工厂开始生产宝马车和需要的空调。
最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车.
这就是工厂模式。
# 分类
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂模式可以分为三类:
1)简单工厂模式(Simple Factory)
2)工厂方法模式(Factory Method)
3)抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。
GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。
> 将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
## 简单工厂模式
### 基本定义
简单工厂模式又称之为静态工厂方法,属于创建型模式。在简单工厂模式中,可以根据传递的参数不同,返回不同类的实例。简单工厂模式定义了一个类,这个类专门用于创建其他类的实例,这些被创建的类都有一个共同的父类。
### 模式结构
![](https://box.kancloud.cn/df7ec9c40eb8d1dbac45a1d25c8b28cf_414x413.png)
### 组成
* Factory:工厂角色。专门用于创建实例类的工厂,提供一个方法,该方法根据传递的参数不同返回不同类的具体实例。
* Product:抽象产品角色。为所有产品的父类。
* ConcreteProduct:具体的产品角色。
简单工厂模式将对象的创建和对象本身业务处理分离了,可以降低系统的耦合度,使得两者修改起来都相对容易些。当以后实现改变时,只需要修改工厂类即可。
### 代码实现
模式场景:在一个披萨店中,要根据不同客户的口味,生产不同的披萨,如素食披萨、希腊披萨等披萨。
该例的UML结构图如下:
![](https://box.kancloud.cn/df027ee852ffa74f635a1fb367176881_699x361.png)
Pizza制造工厂:SimplyPizzaFactory.java
~~~
/**
* 专门用于创建披萨的工厂类
*/
public class SimplePizzaFactory {
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}
else if(type.equals("clam")){
pizza = new ClamPizza();
}
else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
else if(type.equals("veggie")){
pizza = new VeggiePizze();
}
return pizza;
}
}
~~~
抽象披萨:Pizza.java
~~~
/**
* 抽象pizza类
*/
public abstract class Pizza {
public abstract void prepare();
public abstract void bake();
public abstract void cut();
public abstract void box();
}
~~~
具体披萨:CheesePizza.java
~~~
public class CheesePizza extends Pizza{
@Override
public void bake() {
System.out.println("bake CheesePizza ...");
}
@Override
public void box() {
System.out.println("box CheesePizza ...");
}
@Override
public void cut() {
System.out.println("cut CheesePizza ...");
}
@Override
public void prepare() {
System.out.println("prepare CheesePizza ...");
}
}
~~~
PizzaStore.java
~~~
public class PizzaStore {
SimplePizzaFactory factory; //SimplePizzaFactory的引用
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type); //使用工厂对象的创建方法,而不是直接new。这里不再使用具体实例化
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
~~~
### 优点
* 1、简单工厂模式实现了对责任的分割,提供了专门的工厂类用于创建对象。
* 2、客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
* 3、通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
### 缺点
* 1、由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
* 2、使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
* 3、系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
* 4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
### 使用场景
* 1、 工厂类负责创建的对象比较少。
* 2、 客户端只知道传入工厂类的参数,对于如何创建对象不关心。
### 总结
* 1、 简单工厂模式的要点就在于当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
* 2、 简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,但是如果产品过多时,会导致工厂代码非常复杂。
## 工厂方法模式
### 引入
在披萨实例中,如果我想根据地域的不同生产出不同口味的披萨,如纽约口味披萨,芝加哥口味披萨。如果利用简单工厂模式,我们需要两个不同的工厂,NYPizzaFactory、ChicagoPizzaFactory。在该地域中有很多的披萨店,他们并不想依照总店的制作流程来生成披萨,而是希望采用他们自己的制作流程。这个时候如果还使用简单工厂模式,因为简单工厂模式是将披萨的制作流程完全承包了。那么怎么办?
我们可以这样解决:将披萨的制作方法交给各个披萨店完成,但是他们只能提供制作完成的披萨,披萨的订单处理仍然要交给披萨工厂去做。也就是说,我们将createPizza()方法放回到PizzaStore中,其他的部分还是保持不变。
### 基本定义
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式让实例化推迟到子类。
### 模式结构
工厂方法模式的UML结构图:
![](https://box.kancloud.cn/dbf4e880cd16c9c83b7e1d6e0b57567d_497x323.png)
Product:抽象产品。所有的产品必须实现这个共同的接口,这样一来,使用这些产品的类既可以引用这个接口。而不是具体类。
ConcreteProduct:具体产品。
Creator:抽象工厂。它实现了所有操纵产品的方法,但不实现工厂方法。Creator所有的子类都必须要实现factoryMethod()方法。
ConcreteCreator:具体工厂。制造产品的实际工厂。它负责创建一个或者多个具体产品,只有ConcreteCreator类知道如何创建这些产品。
工厂方法模式是简单工厂模式的延伸。在工厂方法模式中,核心工厂类不在负责产品的创建,而是将具体的创建工作交给子类去完成。也就是后所这个核心工厂仅仅只是提供创建的接口,具体实现方法交给继承它的子类去完成。当我们的系统需要增加其他新的对象时,我们只需要添加一个具体的产品和它的创建工厂即可,不需要对原工厂进行任何修改,这样很好地符合了“开闭原则”。
### 代码实现
针对上面的解决方案,得到如下UML结构图:
![](https://box.kancloud.cn/16ddc455b6b267e7eda26e417f613b48_804x377.png)
抽象产品类:Pizza.java
~~~
public abstract class Pizza {
protected String name; //名称
protected String dough; //面团
protected String sause; //酱料
protected List<String> toppings = new ArrayList<String>(); //佐料
public void prepare() {
System.out.println("Preparing "+name);
System.out.println("Tossing dough");
System.out.println("Adding sause");
System.out.println("Adding toppings");
for(int i = 0;i < toppings.size();i++){
System.out.println(" "+toppings.get(i));
}
}
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName(){
return name;
}
}
~~~
具体产品类:NYStyleCheesePizza.java
~~~
public class NYStyleCheesePizza extends Pizza{
public NYStyleCheesePizza(){
name = "Ny Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sause = "Marinara Sauce";
toppings.add("Crated Reggiano Cheese");
}
}
~~~
ChicagoStyleCheesePizza.java
~~~
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza(){
name = "Chicago Style Deep Dish Cheese Pizza";
dough = "Extra Thick Crust Dough";
sause = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
public void cut(){
System.out.println("Cutting the Pizza into square slices");
}
}
~~~
抽象工厂:披萨总店。PizzaStore.java
~~~
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/*
* 创建pizza的方法交给子类去实现
*/
abstract Pizza createPizza(String type);
}
~~~
具体工厂。披萨分店。NYPizzaStore.java
~~~
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String item) {
Pizza pizza = null;
if("cheese".equals(item)){
pizza = new NYStyleCheesePizza();
}
else if("veggie".equals(item)){
pizza = new NYStyleVeggiePizza();
}
else if("clam".equals(item)){
pizza = new NYStyleClamPizza();
}
else if("pepperoni".equals(item)){
pizza = new NYStylePepperoniPizza();
}
return pizza;
}
~~~
ChicagoPizzaStore.java
~~~
public class ChicagoPizzaStore extends PizzaStore {
Pizza createPizza(String type) {
Pizza pizza = null;
if("cheese".equals(type)){
pizza = new ChicagoStyleCheesePizza();
}
else if("clam".equals(type)){
pizza = new ChicagoStyleClamPizza();
}
else if("pepperoni".equals(type)) {
pizza = new ChicagoStylePepperoniPizza();
}
else if("veggie".equals(type)){
pizza = new ChicagoStyleVeggiePizza();
}
return pizza;
}
}
~~~
PizzaTestDrive.java
~~~
public class PizzaTestDrive {
public static void main(String[] args) {
System.out.println("---------Joel 需要的芝加哥的深盘披萨---------");
ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore(); //建立芝加哥的披萨店
Pizza joelPizza =chicagoPizzaStore.orderPizza("cheese"); //下订单
System.out.println("Joel ordered a " + joelPizza.getName() + "\n");
System.out.println("---------Ethan 需要的纽约风味的披萨---------");
NYPizzaStore nyPizzaStore = new NYPizzaStore();
Pizza ethanPizza = nyPizzaStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + ethanPizza.getName() + "\n");
}
}
~~~
### 优点
* 1、 在工厂方法中,用户只需要知道所要产品的具体工厂,无须关系具体的创建过程,甚至不需要具体产品类的类名。
* 2、 在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。
### 缺点
* 1、每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
### 适用场景
* 1、一个类不知道它所需要的对象的类。在工厂方法模式中,我们不需要具体产品的类名,我们只需要知道创建它的具体工厂即可。
* 2、一个类通过其子类来指定创建那个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
* 3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。
### 总结
* 1、工厂方法模式完全符合“开闭原则”。
* 2、工厂方法模式使用继承,将对象的创建委托给子类,通过子类实现工厂方法来创建对象。
* 3、工厂方法允许类将实例化延伸到子类进行。
* 4、工厂方法让子类决定要实例化的类时哪一个。在这里我们要明白这并不是工厂来决定生成哪种产品,而是在编写创建者类时,不需要知道实际创建的产品是哪个,选择了使用哪个子类,就已经决定了实际创建的产品时哪个了。
* 5、在工厂方法模式中,创建者通常会包含依赖于抽象产品的代码,而这些抽象产品是、由子类创建的,创建者不需要真的知道在制作哪种具体产品。
- java
- 设计模式
- 设计模式总览
- 设计原则
- 工厂方法模式
- 抽象工厂模式
- 单例模式
- 建造者模式
- 原型模式
- 适配器模式
- 装饰者模式
- 代理模式
- 外观模式
- 桥接模式
- 组合模式
- 享元模式
- 策略模式
- 模板方法模式
- 观察者模式
- 迭代子模式
- 责任链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
- 附录
- JVM相关
- JVM内存结构
- Java虚拟机的内存组成以及堆内存介绍
- Java堆和栈
- 附录-数据结构的堆栈和内存分配的堆区栈区的区别
- Java内存之Java 堆
- Java内存之虚拟机和内存区域概述
- Java 内存之方法区和运行时常量池
- Java 内存之直接内存(堆外内存)
- JAVA内存模型
- Java内存模型介绍
- 内存模型如何解决缓存一致性问题
- 深入理解Java内存模型——基础
- 深入理解Java内存模型——重排序
- 深入理解Java内存模型——顺序一致性
- 深入理解Java内存模型——volatile
- 深入理解Java内存模型——锁
- 深入理解Java内存模型——final
- 深入理解Java内存模型——总结
- 内存可见性
- JAVA对象模型
- JVM内存结构 VS Java内存模型 VS Java对象模型
- Java的对象模型
- Java的对象头
- HotSpot虚拟机
- HotSpot虚拟机对象探秘
- 深入分析Java的编译原理
- Java虚拟机的锁优化技术
- 对象和数组并不是都在堆上分配内存的
- 垃圾回收
- JVM内存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用实例介绍
- JVM内存回收理论与实现(对象存活的判定)
- JVM参数及调优
- CMS GC日志分析
- JVM实用参数(一)JVM类型以及编译器模式
- JVM实用参数(二)参数分类和即时(JIT)编译器诊断
- JVM实用参数(三)打印所有XX参数及值
- JVM实用参数(四)内存调优
- JVM实用参数(五)新生代垃圾回收
- JVM实用参数(六) 吞吐量收集器
- JVM实用参数(七)CMS收集器
- JVM实用参数(八)GC日志
- Java性能调优原则
- JVM 优化经验总结
- 面试题整理
- 面试题1
- java日志规约
- Spring安全
- OAtuth2.0简介
- Spring Session 简介(一)
- Spring Session 简介(二)
- Spring Session 简介(三)
- Spring Security 简介(一)
- Spring Security 简介(二)
- Spring Security 简介(三)
- Spring Security 简介(四)
- Spring Security 简介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security对比
- Shiro简介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux