Java设计模式之工厂模式

工厂模式

认识工厂模式

所有的工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象

是什么,来达到将对象的创过封装的目的。

简单工厂

定义

以下引用自**《head first 设计模式》**书中对简单工厂的描述

简单工厂其实不是一个设计模式,反而比较像是- -种编程习惯。

但由于经常被使用,所以我们给它一一个"Head First Pattern荣誉奖"。

有些开发人员的确是把这个编程习惯误认为是“工厂模式”(Factory Pattern) 。

当你下次和另一个开发人员之间无话可说的时候,这应当是打破沉默的一个不错的话题。

使用场景

以下引用来自文末参考1

对于产品种类相对较少的情况,考虑使用简单工厂模式。

使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

优点和缺点

以下引用来自文末参考1

优点:

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。
  3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点:

  1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
  2. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  3. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
  4. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

一个简单工厂–披萨工厂的实现

以下代码来自《head first 设计模式》实例

具体的Pizza通过构造函数产生

public class ClamPizza extends Pizza {
   public ClamPizza() {
      name = "Clam Pizza";
      dough = "Thin crust";
      sauce = "White garlic sauce";
      toppings.add("Clams");
      toppings.add("Grated parmesan cheese");
   }
}
public class SimplePizzaFactory {
   //根据不同情况产生不同的pizza对象
   public Pizza createPizza(String type) {
      Pizza pizza = null;

      if (type.equals("cheese")) {
         pizza = new CheesePizza();
      } else if (type.equals("pepperoni")) {
         pizza = new PepperoniPizza();
      } else if (type.equals("clam")) {
         pizza = new ClamPizza();
      } else if (type.equals("veggie")) {
         pizza = new VeggiePizza();
      }
      return pizza;
   }
}

以上代码中CheesePizza,PepperoniPizza,VeggiePizza都有一个相同的父类

abstract public class Pizza {
   String name;
   String dough;
   String sauce;
   List<String> toppings = new ArrayList<String>();

   public String getName() {
      return name;
   }

   public void prepare() {
      System.out.println("Preparing " + name);
   }

   public void bake() {
      System.out.println("Baking " + name);
   }

   public void cut() {
      System.out.println("Cutting " + name);
   }

   public void box() {
      System.out.println("Boxing " + name);
   }

   @Override
   public String toString() {
      // code to display pizza name and ingredients
      StringBuffer display = new StringBuffer();
      display.append("---- " + name + " ----\n");
      display.append(dough + "\n");
      display.append(sauce + "\n");
      for (String topping : toppings) {
         display.append(topping + "\n");
      }
      return display.toString();
   }
}

使用SimplePizzaFactory生产对象

public class PizzaStore {
   SimplePizzaFactory factory;
 
   public PizzaStore(SimplePizzaFactory factory) { 
      this.factory = factory;
   }
   //处理订单,生产pizza 在PizzaStore中完成
   public Pizza orderPizza(String type) {
      Pizza pizza;
 
      pizza = factory.createPizza(type);
 
      pizza.prepare();
      pizza.bake();
      pizza.cut();
      pizza.box();

      return pizza;
   }

}

工厂方法模式

定义

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

到子类。

区别

简单工厂把全部的事情,在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现。

比方说,在工厂方法中,orderPizza()方 法提供了一-般的框架,以便创建比萨,orderPizza()方 法依赖工厂方

法创建具体类,并制造出实际的比萨。可通过继承PizzaStore类,决定实际制造出的比萨是什么。简单工厂的

做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的

产品。

使用场景

以下引用来自文末参考2

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌

优点和缺点

以下引用来自文末参考2

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:

  • 类的个数容易过多,增加复杂度
  • 增加了系统的抽象性和理解难度
  • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

实现

以下代码来自《head first 设计模式》实例

public class NYStyleCheesePizza extends Pizza {

	public NYStyleCheesePizza() { 
		name = "NY Style Sauce and Cheese Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
	}
}

PizzaStore不决定具体的披萨类型。由其子类决定

public abstract class PizzaStore {
 
   protected abstract Pizza createPizza(String item);
 
   public Pizza orderPizza(String type) {
      Pizza pizza = createPizza(type);
      System.out.println("--- Making a " + pizza.getName() + " ---");
      pizza.prepare();
      pizza.bake();
      pizza.cut();
      pizza.box();
      return pizza;
   }
}

PizzaStore的子类NYPizzaStore

public class NYPizzaStore extends PizzaStore {

	@Override
	Pizza createPizza(String item) {
		if (item.equals("cheese")) {
			return new NYStyleCheesePizza();
		} else if (item.equals("veggie")) {
			return new NYStyleVeggiePizza();
		} else if (item.equals("clam")) {
			return new NYStyleClamPizza();
		} else if (item.equals("pepperoni")) {
			return new NYStylePepperoniPizza();
		} else {
			return null;
		}
	}
}

抽象工厂

定义

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户使用抽象的接口来创建-组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。

这样一来,客户就从具体的产品中被解耦。

抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族

区别

工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动

物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

使用场景

以下引用来自文末参考3

抽象工厂模式通常适用于以下场景:

  1. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  2. 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

优点和缺点

以下引用来自文末参考3

抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。

  • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
  • 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

实现

以下代码来自《head first 设计模式》实例

ClamPizza不是通过传入一个工厂出产生

一个工厂的接口:

public interface PizzaIngredientFactory {
 
   public Dough createDough();
   public Sauce createSauce();
   public Cheese createCheese();
   public Veggies[] createVeggies();
   public Pepperoni createPepperoni();
   public Clams createClam();
 
}
public class ClamPizza extends Pizza {
   PizzaIngredientFactory ingredientFactory;
 
   public ClamPizza(PizzaIngredientFactory ingredientFactory) {
      this.ingredientFactory = ingredientFactory;
   }
 
   @Override
   void prepare() {
      System.out.println("Preparing " + name);
      dough = ingredientFactory.createDough();
      sauce = ingredientFactory.createSauce();
      cheese = ingredientFactory.createCheese();
      clam = ingredientFactory.createClam();
   }
}

定义一个抽象的(abstract)PizzaStore

public abstract class PizzaStore {
 
	abstract Pizza createPizza(String item);
 
	public Pizza orderPizza(String type) {
		Pizza pizza = createPizza(type);
		System.out.println("--- Making a " + pizza.getName() + " ---");
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

实现接口的具体工厂:

public class ChicagoPizzaIngredientFactory 
   implements PizzaIngredientFactory 
{

   @Override
   public Dough createDough() {
      return new ThickCrustDough();
   }

   @Override
   public Sauce createSauce() {
      return new PlumTomatoSauce();
   }

   @Override
   public Cheese createCheese() {
      return new MozzarellaCheese();
   }

   @Override
   public Veggies[] createVeggies() {
      Veggies veggies[] = { new BlackOlives(), 
                            new Spinach(), 
                            new Eggplant() };
      return veggies;
   }

   @Override
   public Pepperoni createPepperoni() {
      return new SlicedPepperoni();
   }

   @Override
   public Clams createClam() {
      return new FrozenClams();
   }
}

使用具体工厂来定义ChicagoPizzaStore

public class ChicagoPizzaStore extends PizzaStore {

	@Override
    protected Pizza createPizza(String item) {
		Pizza pizza = null;
		PizzaIngredientFactory ingredientFactory =
		new ChicagoPizzaIngredientFactory();

		if (item.equals("cheese")) {

			pizza = new CheesePizza(ingredientFactory);
			pizza.setName("Chicago Style Cheese Pizza");

		} else if (item.equals("veggie")) {

			pizza = new VeggiePizza(ingredientFactory);
			pizza.setName("Chicago Style Veggie Pizza");

		} else if (item.equals("clam")) {

			pizza = new ClamPizza(ingredientFactory);
			pizza.setName("Chicago Style Clam Pizza");

		} else if (item.equals("pepperoni")) {

			pizza = new PepperoniPizza(ingredientFactory);
			pizza.setName("Chicago Style Pepperoni Pizza");

		}
		return pizza;
	}
}
public class PizzaTestDrive {
 
   public static void main(String[] args) {
      PizzaStore nyStore = new NYPizzaStore();
      PizzaStore chicagoStore = new ChicagoPizzaStore();
 
      Pizza pizza = nyStore.orderPizza("cheese");
      System.out.println("Ethan ordered a " + pizza + "\n");
 
      pizza = chicagoStore.orderPizza("cheese");
      System.out.println("Joel ordered a " + pizza + "\n");

      pizza = nyStore.orderPizza("clam");
      System.out.println("Ethan ordered a " + pizza + "\n");
 
      pizza = chicagoStore.orderPizza("clam");
      System.out.println("Joel ordered a " + pizza + "\n");

      pizza = nyStore.orderPizza("pepperoni");
      System.out.println("Ethan ordered a " + pizza + "\n");
 
      pizza = chicagoStore.orderPizza("pepperoni");
      System.out.println("Joel ordered a " + pizza + "\n");

      pizza = nyStore.orderPizza("veggie");
      System.out.println("Ethan ordered a " + pizza + "\n");
 
      pizza = chicagoStore.orderPizza("veggie");
      System.out.println("Joel ordered a " + pizza + "\n");
   }
}

参考

1简单工厂模式

2工厂方法模式(详解版)

3抽象工厂模式(详解版)

4 工厂模式案例详解

5《head first 设计模式》

你可能感兴趣的:(java)