欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

4.Factory Pattern(工厂模式)

程序员文章站 2022-06-19 14:47:00
工厂模式(Factory Pattern)定义: 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 针对实现编程,但是当我们每次使用new时候,不正是在针对实现编程吗?是的,当使用“new”时,你的确实在实例化一个具体类,所以用的确实是实现,而不是接口。代 ......

工厂模式(Factory Pattern)定义:

  定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

  针对实现编程,但是当我们每次使用new时候,不正是在针对实现编程吗?是的,当使用“new”时,你的确实在实例化一个具体类,所以用的确实是实现,而不是接口。代码绑定这具体类会导致代码更脆弱,更缺乏弹性。

  Duck duck                          =                           new GreenDuck();

  使用接口让代码具有弹性                                 但还是得建立具体类的实例

  但我们总是要用new来创建对象吧。是的,在技术上,new没有错,毕竟这是Java基础部分。真正的犯人是我们的老朋友 “改变”,以及它是如何影响new的使用的。

看下面的代码:

4.Factory Pattern(工厂模式)
 1 /*当有一群相关的具体类时,通常会写出这样的代码*/
 2 Duck duck;
 3 
 4 if(picnic){
 5     duck = new GreenDuck();
 6 }else if(hunting){
 7     duck = new DecoyDuck();
 8 }else if(inBathTub){
 9     duck = new RubberDuck();
10 }...//还有更多的实现类,在运行程序时,要通过判断,来确定创建哪个对象
View Code

  针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。如果代码是针对接口编程而写,那么通过多态,它可以与任何新类实现该接口。但是,当代码使用大量的具体类时,等于是自找麻烦,因为一旦加入新的具体类,就必须改变代码。也就是说,你的代码并非 “对修改关闭”。想用新的具体类型来扩展代码,必须重新打开它。所以,遇到问题时候,就应该回到OO设计原则去寻找线索。别忘了,我们的第一个原则用来处理改变,并帮助我们 “找出会变化的方面,把它们从不变的部分分离出来”

举例:

  有一家比萨店,定制比萨的方法如下:

4.Factory Pattern(工厂模式)
 1 Pizza orderPizza(String type){//这只是类中一个方法,将想要定制的比萨的type类型传入,就会制作出相应的比萨
 2     Pizza pizza;
 3     
 4     if(type.equals("cheese")){//根据比萨的type类型,实例化正确的具体类,然后将其赋值给pizza实例变量。
 5         pizza = new CheesePizza();//请注意,这里的任何比萨都必须实现Pizza接口
 6     }else if(type.equals("greek"){
 7         pizza = new GreekPizza();
 8     }else if(type.equals("pepperoni"){
 9         pizza = new PepperoniPizza();
10     }
11     
12     
13     pizza.prepare();//一旦知道要制作什么类型的比萨,那么我们将要进行加工
14     pizza.bake();//这四个方法,分别是制作比萨的四个步骤
15     pizza.cut();
16     pizza.box();
17     
18     return pizza;
19 }
orderPizza

 正如上面所说,我们要制作新的比萨具体类或者删除现有的比萨具体类,都需要打开orderPizza()方法所在的类,来添加或删除里面的实例化。

我们要做的是,找出会变化的方面,把它们从不变的部分分离出来,现在我们将判断制作比萨类型部分从orderPizza()方法中分离出来:

4.Factory Pattern(工厂模式)
 1 Pizza orderPizza(String type){//这只是类中一个方法,将想要定制的比萨的type类型传入,就会制作出相应的比萨
 2     Pizza pizza;
 3     
 4     //原先实例化比萨的部分被分离出去
 5     
 6     pizza.prepare();//一旦知道要制作什么类型的比萨,那么我们将要进行加工
 7     pizza.bake();//这四个方法,分别是制作比萨的四个步骤
 8     pizza.cut();
 9     pizza.box();
10     
11     return pizza;
12 }
orderPizza 4.Factory Pattern(工厂模式)
 1 public class SimplePizzaFactory{
 2     public Pizza createPizza(String type){
 3         Pizza pizza = null;
 4         
 5         if(type.equals("cheese")){//根据比萨的type类型,实例化正确的具体类,然后将其赋值给pizza实例变量。
 6             pizza = new CheesePizza();//请注意,这里的任何比萨都必须实现Pizza接口
 7         }else if(type.equals("pepperoni"){//删除了一些比萨,又增加了一些比萨
 8             pizza = new PepperoniPizza();
 9         }else if(type.equals("clam"){
10             pizza = new ClamPizza();
11         }else if(type.equals("veggie"){
12             pizza = new VeggiePizza();
13         }
14         
15         return pizza;
16     }
17 }
SimplePizzaFactory

  从上面代码可以看出,我们将orderPizza()方法中实例化比萨的部分分离出来,单独放入了SimplePizzaFactory类中。

  那么,关于new的问题还是没有解决,我们还是针对实现编程,只是把针对实现编程的部分搬到了另一个对象中。问题还是存在,没错,确实存在。但是我们已经做到最小化改变代码(以我们现在所学的知识,还不足以脱离new来实例化对象,所以肯定要针对实现编程。若不能避免,那就做到最好!),若以后要修改创建比萨实例部分,就只要打开SimplePizzaFactory类就可以修改。

  现在我们将PizzaStore(比萨店)类实现:

4.Factory Pattern(工厂模式)
 1 public class PizzaStore{
 2     SimplePizzaFactory factory;//比萨工厂对象
 3     
 4     public PizzaStore(SimplePizzaFactory factory){//在构造器中,需要一个工厂对象作为参数
 5         this.factory = factory;
 6     }
 7     
 8     public Pizza orderPizza(String type){
 9         Pizza pizza;
10         
11         pizza = factory.createPizza(type);//将实例化比萨对象的责任交给工厂对象
12         
13         pizza.prepare();//一旦知道要制作什么类型的比萨,那么我们将要进行加工
14         pizza.bake();//这四个方法,分别是制作比萨的四个步骤
15         pizza.cut();
16         pizza.box();
17         
18         return pizza;
19     }
20 }
PizzaStore

上面的就是简单工厂模式,这只是小本生意的开始。我们只开了一家比萨店,现在我们的比萨店出名了,要开跨地区连锁店,规定每个地区开一家。我们做的比萨要符合当地人的口味。于是各个地区的比萨口味又不一样。

  不多说先把Pizza抽象类实现了:

4.Factory Pattern(工厂模式)
 1 import java.util.ArrayList;
 2 
 3 public abstract class Pizza{
 4     String name;//比萨名字
 5     String dough;//面团
 6     String sauce;//酱
 7     ArrayList toppings = new ArrayList();//配料列表
 8     
 9     void prepare(){//准备做比萨方法
10         System.out.println("Preparing " + name);
11         System.out.println("Tossing dough...");
12         System.out.println("Adding sauce...");
13         System.out.println("Adding toppings: ");
14         
15         for(int i = 0; i < toppings.size(); i++){
16             System.out.println("   " + toppings.get(i));
17         }
18     }
19     
20     void bake(){//烘焙方法
21         System.out.println("Bake for 25 minutes at 350");
22     }
23     
24     void cut(){//切块方法
25         System.out.println("Cutting the pizza into diagonal slices");
26     }
27     
28     void box(){//装盒方法
29         System.out.println("Place pizza in official PizzaStore box");
30     }
31     
32     public String getName(){//获取名称方法
33         return name;
34     }
35 }
Pizza

  然后根据各地区做出不同的Pizza:

4.Factory Pattern(工厂模式)
1 public class NYStyleCheesePizza extends Pizza{//NewYork比萨
2     public NYStyleCheesePizza(){
3         name = "NY Style Sauce and Cheese Pizza";
4         dough = "Thin Crust Dough";
5         sauce = "Marinara Sauce";
6         
7         toppings.add("Grated Reggiano Cheese");
8     }
9 }
NYStyleCheesePizza 4.Factory Pattern(工厂模式)
 1 public class ChicagoStyleCheesePizza extends Pizza{//Chicago比萨
 2     public ChicagoStyleCheesePizza(){
 3         name = "Chicago Style Deep Dish Cheese Pizza";
 4         dough = "Extra Thick Crust Dough";
 5         sauce = "Plum Tomato Sauce";
 6         
 7         toppings.add("Shredded Mozzarella Cheese");
 8     }
 9     
10     void cut(){
11         System.out.println("Cutting the pizza into square slices");
12     }
13 }
ChicagoStyleCheesePizza

  实现PizzaStore抽象类:

4.Factory Pattern(工厂模式)
 1 public abstract class PizzaStore{//所有地区的PizzaStore都要继承该抽象类
 2     public Pizza orderPizza(String type){//定制Pizza方法
 3         Pizza pizza;
 4         
 5         pizza = createPizza(type);//创建Pizza实例
 6         
 7         pizza.prepare();
 8         pizza.bake();
 9         pizza.cut();
10         pizza.box();
11         
12         return pizza;
13     }
14     
15     abstract Pizza createPizza(String type);//创建Pizza实例的方法由每个地区的PizzaStore子类来实现
16 }
PizzaStore

  每个地区开一个PizzaStore:

4.Factory Pattern(工厂模式)
1 public class NYPizzaStore extends PizzaStore{//NewYork店
2     Pizza createPizza(String item){
3         if(item.equals("cheese")){//可以制作出cheese口味的Pizza
4             return new NYStyleCheesePizza();
5         }else return null;//也可以制作出其他口味的Pizza,我就不实现了。
6     }
7 }
NYPizzaStore 4.Factory Pattern(工厂模式)
1 public class ChicagoPizzaStore extends PizzaStore{//Chicago店
2     Pizza createPizza(String item){
3         if(item.equals("cheese")){//可以制作出Cheese口味的Pizza
4             return new ChicagoStyleCheesePizza();
5         }else return null;//也可以制作出其他口味的Pizza,我就不实现了。
6     }
7 }
ChicagoPizzaStore

  测试类:

4.Factory Pattern(工厂模式)
 1 public class PizzaTestDrive{
 2     
 3     public static void main(String args[]){
 4         //先开店
 5         PizzaStore nyStore = new NYPizzaStore();
 6         PizzaStore chicagoStore = new ChicagoPizzaStore();
 7         
 8         //NYPizzaStore店制作出cheese口味的Pizza
 9         Pizza pizza = nyStore.orderPizza("cheese");
10         System.out.println("Ethan ordered a " + pizza.getName() + "\n");
11         //ChicagoPizzaStore店制作出cheese口味的Pizza
12         pizza = chicagoStore.orderPizza("cheese");
13         System.out.println("Joel ordered a " + pizza.getName() + "\n");
14     }
15 }
PizzaTestDrive

编译运行结果:

4.Factory Pattern(工厂模式)

   上面的就是工厂模式。

总结一下,有两个抽象类:

1.Pizza抽象类:这个抽象类是产品抽象类,换句话说就是工厂要制作出的产品。所有的Pizza子类都要继承自该Pizza抽象类。

2.PizzaStore抽象类:这个抽象类是工厂抽象类,这个抽象类中有一个createPizza()抽象方法,这个方法就是为了让子类去实现自己的产品类。这样一来,子类可以根据自己的情况,随心所欲的生产符合自己口味的产品。

再细细地品味一下工厂模式的定义:

  工厂模式定义了一个创建对象的接口(工厂抽象类),但由工厂子类决定要实例化哪一个类。工厂方法让类把实例化推迟到了工厂子类。

由工厂子类决定要实例化哪一个类,不要理解错误。并不是指模式允许子类本身在运行时做决定,而是指在编写工厂抽象类时,不需要知道实际创建的产品是哪一个。选择使用了哪个子类,自然就决定了实际创建的产品是什么。

这里温馨提醒一下:在设计模式中,所谓的 “实现一个接口” 并 “不一定” 表示 “写一个接口,并利用implements关键字来实现这个接口”。“实现一个接口” 泛指 “实现某个超类型(可以是类或接口)的某个方法” 。