设计模式目录:
概述:
第1部分 简单工厂模式
在设计原则中有这样一句话“我们应该针对接口编程,而不是针对实现编程”。但是我们还是在一直使用new关键字来创建一个对象,这不就是在针对实现编程么?
针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。入股代码是针对接口而写,那么可以通过多态,它可以与任何新类实现该接口。但是,当代码使用一大堆的具体类时,等于是自找麻烦,因为一旦加入新的具体类,就必须要改变代码。在这里我们希望能够调用一个简单的方法,我传递一个参数过去,就可以返回给我一个相应的具体对象,这个时候我们就可以使用简单工厂模式。
1.1 基本定义
简单工厂模式又称之为静态工厂方法,属于创建型模式。在简单工厂模式中,可以根据传递的参数不同,返回不同类的实例。简单工厂模式定义了一个类,这个类专门用于创建其他类的实例,这些被创建的类都有一个共同的父类。
简单工厂模式其实不是一个设计模式,反而比较像是一种编程习惯。但由于经常被使用,有些开发人员的确是把这个编程习惯误认为是“工厂模式”。
模式场景:在一个披萨店中,要根据不同客户的口味,生产不同的披萨,如素食披萨、希腊披萨等披萨。
1.2 简单工厂模式实现
代码实现:
Pizza制造工厂:SimplyPizzaFactory.java
1 /** 2 * 专门用于创建披萨的工厂类 3 */ 4 public class SimplePizzaFactory { 5 public Pizza createPizza(String type){ 6 Pizza pizza = null; 7 8 if(type.equals("cheese")){ 9 pizza = new CheesePizza(); 10 } 11 else if(type.equals("clam")){ 12 pizza = new ClamPizza(); 13 } 14 else if(type.equals("pepperoni")){ 15 pizza = new PepperoniPizza(); 16 } 17 else if(type.equals("veggie")){ 18 pizza = new VeggiePizze(); 19 } 20 21 return pizza; 22 } 23 }
抽象披萨:Pizza.java
1 /** 2 * 抽象pizza类 3 */ 4 public abstract class Pizza { 5 public abstract void prepare(); 6 7 public abstract void bake(); 8 9 public abstract void cut(); 10 11 public abstract void box(); 12 }
具体披萨:CheesePizza.java:
1 public class CheesePizza extends Pizza{ 2 3 @Override 4 public void bake() { 5 System.out.println("bake CheesePizza ..."); 6 } 7 8 @Override 9 public void box() { 10 System.out.println("box CheesePizza ..."); 11 } 12 13 @Override 14 public void cut() { 15 System.out.println("cut CheesePizza ..."); 16 } 17 18 @Override 19 public void prepare() { 20 System.out.println("prepare CheesePizza ..."); 21 } 22 23 }
PizzaStore.java
1 public class PizzaStore { 2 SimplePizzaFactory factory; //SimplePizzaFactory的引用 3 public PizzaStore(SimplePizzaFactory factory){ 4 this.factory = factory; 5 } 6 7 public Pizza orderPizza(String type){ 8 Pizza pizza; 9 pizza = factory.createPizza(type); //使用工厂对象的创建方法,而不是直接new。这里不再使用具体实例化 10 11 pizza.prepare(); 12 pizza.bake(); 13 pizza.cut(); 14 pizza.box(); 15 16 return pizza; 17 } 18 }
1.3 优缺点和使用场景
优点
1、简单工厂模式实现了对责任的分割,提供了专门的工厂类用于创建对象。
2、客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
3、通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
1、由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
2、使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
3、系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
使用场景
1、 工厂类负责创建的对象比较少。
2、 客户端只知道传入工厂类的参数,对于如何创建对象不关心。
第2部分 工厂模式
2.1 问题引入
在第1部分中通过披萨的实例介绍了简单工厂模式。在披萨实例中,如果我想根据地域的不同生产出不同口味的披萨,如纽约口味披萨,芝加哥口味披萨。如果利用简单工厂模式,我们需要两个不同的工厂,NYPizzaFactory、ChicagoPizzaFactory。在该地域中有很多的披萨店,他们并不想依照总店的制作流程来生成披萨,而是希望采用他们自己的制作流程。这个时候如果还使用简单工厂模式,因为简单工厂模式是将披萨的制作流程完全承包了。那么怎么办?
我们可以这样解决:将披萨的制作方法交给各个披萨店完成,但是他们只能提供制作完成的披萨,披萨的订单处理仍然要交给披萨工厂去做。也就是说,我们将createPizza()方法放回到PizzaStore中,其他的部分还是保持不变。
2.2 基本定义
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式让实例化推迟到子类。
所谓的“决定”,并不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品时哪一个。选择了使用哪个子类,自然就决定了实际创建的产品是什么。
2.3 工厂方法模式实现
抽象产品类:Pizza.java
1 package firsthead.factorypattern; 2 3 import java.util.ArrayList; 4 5 /** 6 * 7 * @ClassName: Pizza 8 * TODO 9 * @author Xingle 10 * @date 2014-8-22 下午3:45:05 11 */ 12 public class Pizza { 13 //名称 14 String name; 15 //面团 16 String dough; 17 //酱料 18 String sauce; 19 //佐料 20 ArrayList toppings = new ArrayList(); 21 22 void prepare() { 23 System.out.println("Preparing " + name); 24 System.out.println("Tossing dough..."); 25 System.out.println("Adding sauce..."); 26 System.out.println("Adding toppings: "); 27 for (int i = 0; i < toppings.size(); i++) { 28 System.out.println(" " + toppings.get(i)); 29 } 30 } 31 public String getName() { 32 return name; 33 } 34 35 public void bake(){ 36 System.out.println("Bake for 25 minutes at 350"); 37 } 38 39 public void cut() { 40 System.out.println("Cutting the pizza into diagonal slices"); 41 } 42 43 public void box() { 44 System.out.println("Place pizza in official PizzaStore box"); 45 } 46 47 public String toString(){ 48 StringBuffer disPlay = new StringBuffer(); 49 disPlay.append("---- " + name + " ----\n"); 50 disPlay.append(dough + "\n"); 51 disPlay.append(sauce + "\n"); 52 for (int i = 0; i < toppings.size(); i++) { 53 disPlay.append((String )toppings.get(i) + "\n"); 54 } 55 return disPlay.toString(); 56 } 57 58 }
具体产品类:NYStyleCheesePizza.java
1 public class NYStyleCheesePizza extends Pizza{ 2 3 public NYStyleCheesePizza(){ 4 name = "NY Style Sauce and Cheese Pizza"; 5 dough = "Thin Crust Dough"; 6 sauce = "Marinara Sauce"; 7 toppings.add("Grated Reggiano Cheese"); 8 } 9 }
具体产品类:ChicagoStyleCheesePizza .java
1 public class ChicagoStyleCheesePizza extends Pizza{ 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 public void cut() { 11 System.out.println("Cutting the pizza into square slices"); 12 } 13 }
抽象工厂:披萨总店。PizzaStore.java
1 package firsthead.factorypattern; 2 3 /** 4 * 披萨商店 5 * @ClassName: PizzaStore 6 * TODO 7 * @author Xingle 8 * @date 2014-8-22 下午2:49:46 9 */ 10 public abstract class PizzaStore { 11 12 //订购Pizza 13 public Pizza orderPizza(String type){ 14 Pizza pizza; 15 16 //pizza = factory.createPizza(type); 17 //从工厂对象中移回 18 pizza = createPizza(type); 19 pizza.prepare(); 20 pizza.bake(); 21 pizza.cut(); 22 pizza.box(); 23 24 return pizza; 25 } 26 27 //实例化披萨的责任被移到一个“方法”中,此方法就如同是一个工厂 28 //创建pizza的方法交给子类去实现 29 protected abstract Pizza createPizza(String type); 30 }
具体工厂。披萨分店。NYPizzaStore.java
1 package firsthead.factorypattern; 2 3 /** 4 * 加盟店——纽约披萨商店 5 * @ClassName: NYPizzaStore 6 * TODO 7 * @author Xingle 8 * @date 2014-8-22 下午4:12:03 9 */ 10 public class NYPizzaStore extends PizzaStore{ 11 12 /** 13 * 14 * @Description: 15 * @param item 16 * @return 17 * @author xingle 18 * @data 2014-8-22 下午4:16:21 19 */ 20 @Override 21 protected 22 Pizza createPizza(String item) { 23 if (item.equals("cheese")) { 24 return new NYStyleCheesePizza(); 25 } else if (item.equals("veggie")) { 26 return new NYStyleVeggiePizza(); 27 } else if (item.equals("clam")) { 28 return new NYStyleClamPizza(); 29 } else if (item.equals("pepperoni")) { 30 return new NYStylePepperoniPizza(); 31 } else return null; 32 } 33 34 35 }
具体工厂。披萨分店。ChicagoPizzaStore.java
1 package firsthead.factorypattern; 2 3 /** 4 * 加盟店——芝加哥披萨商店 5 * @ClassName: ChicagoPizzaStore 6 * TODO 7 * @author Xingle 8 * @date 2014-8-22 下午4:18:47 9 */ 10 public class ChicagoPizzaStore extends PizzaStore{ 11 12 /** 13 * 14 * @Description: TODO 15 * @param item 16 * @return 17 * @author xingle 18 * @data 2014-8-22 下午4:19:35 19 */ 20 @Override 21 protected 22 Pizza createPizza(String item) { 23 if (item.equals("cheese")) { 24 return new ChicagoStyleCheesePizza(); 25 } else if (item.equals("veggie")) { 26 return new ChicagoStyleVeggiePizza(); 27 } else if (item.equals("clam")) { 28 return new ChicagoStyleClamPizza(); 29 } else if (item.equals("pepperoni")) { 30 return new ChicagoStylePepperoniPizza(); 31 } else return null; 32 } 33 34 }
最后测试程序,PizzaTestDrive.java
1 package firsthead.factorypattern; 2 3 /** 4 * 测试程序 5 * @ClassName: PizzaTestDrive 6 * TODO 7 * @author Xingle 8 * @date 2014-8-22 下午4:38:46 9 */ 10 public class PizzaTestDrive { 11 public static void main(String[] args){ 12 PizzaStore nyStore = new NYPizzaStore(); 13 PizzaStore chicageStore = new ChicagoPizzaStore(); 14 15 System.out.println("---------Ethan 需要的纽约风味的披萨---------"); 16 Pizza pizza = nyStore.orderPizza("cheese"); 17 System.out.println("Ethan ordered a "+pizza.getName()+"\n"); 18 19 System.out.println("---------Joel 需要的芝加哥的深盘披萨---------"); 20 pizza = chicageStore.orderPizza("cheese"); 21 System.out.println("Joel ordered a "+pizza.getName()); 22 } 23 }
执行结果:
2.4 工厂方法模式优缺点和应用场景
优点
1、 在工厂方法中,用户只需要知道所要产品的具体工厂,无须关系具体的创建过程,甚至不需要具体产品类的类名。
2、 在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。
缺点
1、 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
适用场景
1、一个类不知道它所需要的对象的类。在工厂方法模式中,我们不需要具体产品的类名,我们只需要知道创建它的具体工厂即可。
2、一个类通过其子类来指定创建那个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。
第3部分 抽象工厂模式
3.1 问题引入
在工厂方法模式中,我们使用一个工厂创建一个产品,也就是说一个具体的工厂对应一个具体的产品。但是有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。
3.2 基本定义
抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。
3.3 抽象工厂模式实现
依然是披萨店。为了要保证每家加盟店都能够生产高质量的披萨,防止使用劣质的原料,我们打算建造一家生产原料的工厂,并将原料运送到各家加盟店。但是加盟店都位于不同的区域,比如纽约、芝加哥。纽约使用一组原料,芝加哥使用另一种原料。在这里我们可以这样理解,这些不同的区域组成了原料家族,每个区域实现了一个完整的原料家族。
首先创建一个原料工厂。该工厂为抽象工厂,负责创建所有的原料。
PizzaIngredientFactory.java
1 package firsthead.factorypattern; 2 3 /** 4 * 5 * @ClassName: PizzaIngredientFactory 6 * TODO 7 * @author Xingle 8 * @date 2014-8-26 上午10:56:43 9 */ 10 public interface PizzaIngredientFactory { 11 //在接口中,每个原料都有一个对应的方法创建该原料 12 public Dough createDough(); 13 public Sauce createSauce(); 14 public Cheese createCheese(); 15 public Veggies[] createVeggies(); 16 public Pepperoni createPepperoni(); 17 public Clams createClam(); 18 }
原料工厂创建完成之后,需要创建具体的原料工厂。该具体工厂只需要继承PizzaIngredientFactory,然后实现里面的方法即可。
纽约原料工厂:NYPizzaIngredientFactory.java。
1 package firsthead.factorypattern; 2 3 /** 4 * 具体原料工厂——纽约原料工厂 5 * @ClassName: NYPizzaIngredientFactory 6 * TODO 7 * @author Xingle 8 * @date 2014-8-26 上午10:58:17 9 */ 10 public class NYPizzaIngredientFactory implements PizzaIngredientFactory{ 11 12 @Override 13 public Dough createDough() { 14 return new ThinCrustDough(); 15 } 16 17 @Override 18 public Sauce createSauce() { 19 return new MarinaraSauce(); 20 } 21 22 @Override 23 public Cheese createCheese() { 24 return new ReggianoCheese(); 25 } 26 27 @Override 28 public Veggies[] createVeggies() { 29 Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; 30 return veggies; 31 } 32 33 @Override 34 public Pepperoni createPepperoni() { 35 return new SlicedPepperoni(); 36 } 37 38 @Override 39 public Clams createClam() { 40 return new FreshClams(); 41 } 42 43 }
具体原料:
public class ThinCrustDough implements Dough { public String toString() { return "Thin Crust Dough"; } }
public class MarinaraSauce implements Sauce { public String toString() { return "Marinara Sauce"; } }
public class ReggianoCheese implements Cheese { public String toString() { return "Reggiano Cheese"; } }
public class Garlic implements Veggies { public String toString() { return "Garlic"; } }
public class Onion implements Veggies { public String toString() { return "Onion"; } }
public class Mushroom implements Veggies { public String toString() { return "Mushrooms"; } }
public class RedPepper implements Veggies { public String toString() { return "Red Pepper"; } }
public class SlicedPepperoni implements Pepperoni { public String toString() { return "Sliced Pepperoni"; } }
public class FreshClams implements Clams { public String toString() { return "Fresh Clams from Long Island Sound"; } }
public interface Veggies { public String toString(); }
其他类似
重新返回到披萨。在这个披萨类里面,我们需要使用原料,其他方法保持不变,将prepare()方法声明为抽象,在这个方法中,我们需要收集披萨所需要的原料。
Pizza.java
1 package firsthead.factorypattern; 2 3 import java.util.ArrayList; 4 5 /** 6 * 7 * @ClassName: Pizza 8 * TODO 9 * @author Xingle 10 * @date 2014-8-22 下午3:45:05 11 */ 12 public abstract class Pizza { 13 /*每个披萨都持有一组在准备时会用到的原料 */ 14 String name; 15 Dough dough; 16 Sauce sauce; 17 Veggies veggies[]; 18 Cheese cheese; 19 Pepperoni pepperoni; 20 Clams clam; 21 //佐料 22 ArrayList toppings = new ArrayList(); 23 24 25 /** 26 * prepare()方法声明为抽象方法。在这个方法中,我们需要收集披萨所需要的原料,而这些原料都是来自原料工厂 27 */ 28 abstract void prepare(); 29 30 public String getName() { 31 return name; 32 } 33 34 public void setName(String name) { 35 this.name = name; 36 } 37 38 public void bake(){ 39 System.out.println("Bake for 25 minutes at 350"); 40 } 41 42 public void cut() { 43 System.out.println("Cutting the pizza into diagonal slices"); 44 } 45 46 public void box() { 47 System.out.println("Place pizza in official PizzaStore box"); 48 } 49 50 public String toString(){ 51 StringBuffer disPlay = new StringBuffer(); 52 disPlay.append("---- " + name + " ----\n"); 53 disPlay.append(dough + "\n"); 54 disPlay.append(sauce + "\n"); 55 for (int i = 0; i < toppings.size(); i++) { 56 disPlay.append((String )toppings.get(i) + "\n"); 57 } 58 return disPlay.toString(); 59 } 60 61 }
CheesePizza.java
1 package firsthead.factorypattern; 2 3 /** 4 * 5 * @ClassName: CheesePizza 6 * TODO 7 * @author Xingle 8 * @date 2014-8-26 上午11:19:04 9 */ 10 public class CheesePizza extends Pizza{ 11 12 PizzaIngredientFactory ingredientFactory; 13 14 public CheesePizza(PizzaIngredientFactory ingredientFactory){ 15 this.ingredientFactory = ingredientFactory; 16 prepare(); 17 } 18 19 /** 20 * 21 * @Description: 22 * @author xingle 23 * @data 2014-8-26 上午11:27:17 24 */ 25 @Override 26 void prepare() { 27 System.out.println("Preparing " + name); 28 dough = ingredientFactory.createDough(); 29 sauce = ingredientFactory.createSauce(); 30 cheese = ingredientFactory.createCheese(); 31 } 32 33 34 35 }
做完披萨后,需要关注披萨店了。
在披萨店中,我们依然需要关注原料,当地的披萨店需要和本地的原料工厂关联起来。
PizzaStore.java
1 package firsthead.factorypattern; 2 3 /** 4 * 披萨商店 5 * @ClassName: PizzaStore 6 * TODO 7 * @author Xingle 8 * @date 2014-8-22 下午2:49:46 9 */ 10 public abstract class PizzaStore { 11 12 //订购Pizza 13 public Pizza orderPizza(String type){ 14 Pizza pizza; 15 16 pizza = createPizza(type); 17 pizza.prepare(); 18 pizza.bake(); 19 pizza.cut(); 20 pizza.box(); 21 22 return pizza; 23 } 24 25 26 /** 27 * 创建pizza的方法交给子类去实现 28 */ 29 abstract Pizza createPizza(String type); 30 }
纽约的披萨店:NYPizzaStore.java
1 package firsthead.factorypattern; 2 3 /** 4 * 纽约的披萨店: 5 * @ClassName: NYPizzaStore 6 * TODO 7 * @author Xingle 8 * @date 2014-8-26 上午11:39:06 9 */ 10 public class NYPizzaStore extends PizzaStore{ 11 12 /** 13 * 14 * @Description: 15 * @param type 16 * @return 17 * @author xingle 18 * @data 2014-8-26 上午11:39:23 19 */ 20 @Override 21 protected Pizza createPizza(String item) { 22 Pizza pizza = null; 23 //使用纽约的原料工厂 24 PizzaIngredientFactory ingredientFactory = 25 new NYPizzaIngredientFactory(); 26 27 if (item.equals("cheese")) { 28 29 pizza = new CheesePizza(ingredientFactory); 30 pizza.setName("New York Style Cheese Pizza"); 31 32 } else if (item.equals("veggie")) { 33 34 pizza = new VeggiePizza(ingredientFactory); 35 pizza.setName("New York Style Veggie Pizza"); 36 37 } else if (item.equals("clam")) { 38 39 pizza = new ClamPizza(ingredientFactory); 40 pizza.setName("New York Style Clam Pizza"); 41 42 } else if (item.equals("pepperoni")) { 43 44 pizza = new PepperoniPizza(ingredientFactory); 45 pizza.setName("New York Style Pepperoni Pizza"); 46 47 } 48 return pizza; 49 } 50 51 }
最后,测试程序
1 package firsthead.factorypattern; 2 3 /** 4 * 5 * @ClassName: PizzaTestDrive 6 * TODO 7 * @author Xingle 8 * @date 2014-8-26 上午11:37:20 9 */ 10 public class PizzaTestDrive { 11 public static void main(String[] args) { 12 PizzaStore nyStore = new NYPizzaStore(); 13 14 Pizza pizza = nyStore.orderPizza("cheese"); 15 System.out.println("Ethan ordered a " + pizza + "\n"); 16 17 pizza = nyStore.orderPizza("clam"); 18 System.out.println("Ethan ordered a " + pizza + "\n"); 19 20 pizza = nyStore.orderPizza("pepperoni"); 21 System.out.println("Ethan ordered a " + pizza + "\n"); 22 23 pizza = nyStore.orderPizza("veggie"); 24 System.out.println("Ethan ordered a " + pizza + "\n"); 25 26 27 } 28 29 }
执行结果:
Preparing null Preparing New York Style Cheese Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Cheese Pizza ---- Thin Crust Dough Marinara Sauce Preparing null Preparing New York Style Clam Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Clam Pizza ---- Thin Crust Dough Marinara Sauce Preparing null Preparing New York Style Pepperoni Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Pepperoni Pizza ---- Thin Crust Dough Marinara Sauce Preparing null Preparing New York Style Veggie Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Veggie Pizza ---- Thin Crust Dough Marinara Sauce