设计模式:12.工厂模式及其3中实现方式

91 阅读8分钟

工厂模式

1.披萨订购问题的普通解决方案

(1) 问题

披萨店的工作:

  • 披萨的种类很多,比如GreekPizza(希腊披萨)、CheessPizza(奶酪披萨)
  • 披萨的制作过程:prepare(准备)、bake(烘烤)、cut(切割)、box(打包)
  • 披萨店要有订购功能
  • 要便于披萨种类的拓展和维护
(2) 普通解决方法

在这里插入图片描述

/** 披萨抽象类 **/
public abstract class Pizza{
	private String type;	// 披萨的类型

	//准备 抽象方法,不需要实现
	public abstract void prepare();
	//烘烤
	public void bake(){ sout(type + "烘烤中..."); }
	//切割
	public void cut(){ sout(type + "切割中..."); }
	//打包
	public void box(){ sout(type + "打包中...");}
	
	public void setType(String type){ this.type = type; }
}

/** 奶酪披萨 **/
public class CheessPizza extends Pizza{
	@Override
	public void prepare(){ sout("奶酪披萨准备中..."); }
}

/** 希腊披萨 **/
public class GreekPizza extends Pizza{
	@Override
	public void prepare(){ sout("希腊披萨准备中..."); }
}

/** 订购披萨类 **/
public class OrderPizza{
	// 构造器
	public OrderPizza(){
		Pizza pizza = null;
		String pizzaType;	// 订购披萨的类型
		while(true){
			pizzaType = getType();
			if("greek".equals(pizzaType)){
				pizza = new GreekPizza();
				pizza.setType("greek");
			}else if("cheess".equals(pizzaType)){
				pizza = new CheessPizza();
				pizza.setType("cheess");
			}else{
				break;
			}

			// 制作
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
	
	// 从控制台输入披萨种类
	public getType(){
		try{
			BufferedReader str = new BufferedReader(new InputStreamReader(System.in));
			String pizzaType = str.readLine();
			return pizzaType;
		}catch(IOException e){
			return "";
		}
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 产生订购订单
		new OrderPizza();
	}
}
(3) 普通解决方法的优缺点
  • 优点: 简单易操作、容易理解
  • 缺点:违反设计模式的ocp原则:对拓展开放,对修改关闭。即当我们新增功能时,要尽量不修改代码或少修改代码。
    当我们新增一个披萨种类,Pepper胡椒披萨时,需要修改:
    在这里插入图片描述
    这里仅仅是只有一个披萨店的订购功能,如果有多个披萨店的订购功能,OrderPizza2、PrderPizza3这些类都需要修改加入判断胡椒披萨的逻辑:
    在这里插入图片描述
(4) 使用设计模式优化的思路

把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改改类就可,其他用到Pizza的对象的代码就不需要修改了。


2.简单工厂模式

(1)基本介绍
  • 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式中最简单使用的模式。
  • 定义:定义了一个创建对象的类,这个类的功能是:实例化各种对象。
  • 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用工厂模式。
(2)改进披萨订购功能
/** 简单工厂模式:披萨订单工厂类 **/
public class OrderFactory{
	// 根据type创建Pizza对象
	public Pizza createPizza(String type){
		Pizza pizza = null;
		if("greek".equals(type)){
			pizza = new GreekPizza();
			pizza.setType("greek");
		}else if("cheess".equals(type)){
			pizza = new CheessPizza();
			pizza.setType("cheess");
		}
		return pizza ;
	}
}

/** 修改订购披萨类,把披萨订单工厂类聚合进来 **/
public class OrderPizza{
	// 定义一个简单工厂对象
	private OrderFactory orderFactory;
	
	// 因为这里是聚合关系,所以把简单工厂模式传进来,当然,也可以使用组合关系,直接定义简单工厂对象的时候就new OrderFactory(),更简单
	public void OrderPizza(OrderFactory orderFactory){
		this.orderFactory = orderFactory;
		
		while(true){
			String type = getType();
			Pizza pizza = this.orderFactory.createPizza(type);
			if(null == pizza){ break; }
			
			// 制作
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
	
	// 从控制台输入披萨种类
	public getType(){
		try{
			BufferedReader str = new BufferedReader(new InputStreamReader(System.in));
			String pizzaType = str.readLine();
			return pizzaType;
		}catch(IOException e){
			return "";
		}
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 产生订购订单
		new OrderPizza(new OrderFactory());
	}
}

在这里插入图片描述

(3)简单工厂模式的另一种写法:静态工厂模式

就是将静态工厂类的获取对象的方法变成静态方法,使用时直接调用即可,更加简单。

/** 静态工厂模式 **/
public class OrderFactory{
	// 根据type创建Pizza对象
	public static Pizza createPizza(String type){
		Pizza pizza = null;
		...
		return pizza ;
	}
}

/** 修改订购披萨类,把披萨订单工厂类聚合进来 **/
public class OrderPizza{
	// 直接调用工厂类的静态方法获取披萨对象即可
	public void OrderPizza(){
		...
		while(true){
			String type = getType();
			// 直接调用
			Pizza pizza = OrderFactory.createPizza(type);
			...
		}
	}
}

3.工厂方法模式

(1)披萨问题的新增功能

客户在下单披萨时,可以选择发货地区,比如:北京奶酪披萨(BjCheessPizza)、北京希腊披萨(BjGreekPizza)、伦敦奶酪披萨(LdCheessPizza)、伦敦希腊披萨(LdGreekPizza)等。

(2)思路
  • 使用简单工厂模式,创建不同的简单工厂类:BjOrderFactory、LdOrderFactory。但是这种方案,软件的可维护性、可拓展性并不是很好,以后还会增加新的地区。
  • 使用工厂方法模式:将披萨项目的实例化功能抽象成抽象方法,在不同地区点餐子类中具体实现。
(3)基本介绍
  • 定义了一个创建对象的抽象方法,由子类决定要实例化的类。
  • 工厂方法模式将对象的实例化推迟到子类。
  • 简单工厂模式在工厂类中直接实例化了:OrderFactory.createPizza()中直接Pizza pizza = new GreekPizza();
(4)实现

在这里插入图片描述

/** 披萨抽象类 **/
public abstract class Pizza{
	private String type;	// 披萨的类型

	//准备 抽象方法,不需要实现
	public abstract void prepare();
	
	public void bake(){ sout(type + "烘烤中..."); }
	public void cut(){ sout(type + "切割中..."); }
	public void box(){ sout(type + "打包中...");}
	
	public void setType(String type){ this.type = type; }
}

/** 北京奶酪披萨 **/
public class BjCheessPizza extends Pizza{
	@Override
	public void prepare(){
		setType("BJ_Cheess");
		sout("北京_奶酪披萨准备中..."); 
	}
}

/** 北京希腊披萨 **/
public class BjGreekPizza extends Pizza{
	@Override
	public void prepare(){ 
		setType("BJ_Greek");
		sout("北京_希腊披萨准备中...");
	}
}

/** 伦敦奶酪披萨 **/
public class LdCheessPizza extends Pizza{
	@Override
	public void prepare(){
		setType("LD_Cheess");
		sout("伦敦_奶酪披萨准备中..."); 
	}
}

/** 伦敦希腊披萨 **/
public class LdGreekPizza extends Pizza{
	@Override
	public void prepare(){ 
		setType("LD_Greek");
		sout("伦敦_希腊披萨准备中...");
	}
}

/** 披萨订购抽象类 **/
public abstract class OrderPizza{
	// 抽象方法返回Pizza对象,让各个工厂子类自己实现
	abstract Pizza createPizza(String orderType);
	
	// 订购
	public OrderPizza(){
		Pizza pizza = null;
		String orderPizza;
		while(true){
			orderType = getType();
			// 获取Pizza对象,createPizza会由各个工厂子类实现
			pizza = createPizza(orderType);
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
}

/** 北京订购 **/
public class BjOrderPizza extends OrderPizza{
	@Override
	Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new BjCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new BjGreekPizza();
		}
		return pizza;
	}
}

/** 伦敦订购 **/
public class LdOrderPizza extends OrderPizza{
	@Override
	Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new LdCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new LdGreekPizza();
		}
		return pizza;
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 产生订购订单
		new BjOrderPizza();
		new LdOrderPizza();
	}
}

4.抽象工厂模式

(1)基本介绍
  • 定义一个Interface,用于创建相关或有依赖关系的对象簇,而无需指明具体的类。
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
  • 从设计层面看,抽象工厂模式是对简单工厂模式的改进,进一步抽象。
  • 将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将简单的工厂类变成了工厂簇,易于维护。
(2)实现披萨项目

在这里插入图片描述

/** 披萨抽象类 **/
public abstract class Pizza{ ... }

/** 北京奶酪披萨 **/
public class BjCheessPizza extends Pizza{ ... }

/** 北京希腊披萨 **/
public class BjGreekPizza extends Pizza{ ... }

/** 伦敦奶酪披萨 **/
public class LdCheessPizza extends Pizza{ ... }

/** 伦敦希腊披萨 **/
public class LdGreekPizza extends Pizza{ ... }

/** 抽象工厂 **/
public interface AbsFactory{
	// 由工厂子类实现
	public Pizza createPizza(String orderType);
}

/** 北京工厂实现类 **/
public class BjFactory implements AbsFactory{
	@Override
	public Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new BjCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new BjGreekPizza();
		}
		return pizza;
	}
}

/** 伦敦工厂实现类 **/
public class LdFactory implements AbsFactory{
	@Override
	public Pizza createPizza(String orderType){
		Pizza pizza = null;
		if(orderType.equals("cheese")){
			pizza = new LdCheessPizza();
		}else if(orderType.equals("greek")){
			pizza = new LdGreekPizza();
		}
		return pizza;
	}
}

/** 披萨订购店1 **/
public class OrderPizza1{
	// 将工厂抽象接口聚合进来
	private AbsFactory factory;
	
	public OrderPizza1(AbsFactory factory){
		setFactory(factory);
	}
	
	// 传入具体的工厂实例
	public void setFactory(AbsFactory factory){
		Pizza pizza = null;
		String orderType = "";
		this.factory = factory;
		while(true){
			orderType = getType();
			pizza = factory.createPizza();
			 
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
		}
	}
}

/** 使用 **/
public class Do{
	psvm(){
		// 产生订购订单
		new OrderPizza1(new BjFactory());
		new OrderPizza1(new LdFactory());
	}
}
(3)再新增一个上海地区

在这里插入图片描述


5.三种方式如何选择

  • 三种方式差别并不是很大
  • 日常开发过程中,如果实例对象很多,可以使用抽象工厂方法将对象分类创建。如果实例对象并不是那么多,使用简单工厂模式即可。

6.JDK源码分析

Calendar calendar = Calendar.getInstance();

System.out.println("年:"+calendar.get(Calendar.YEAR));
System.out.println("月:"+calendar.get(Calendar.MONTH)+1);
System.out.println("日:"+calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("时:"+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("分:"+calendar.get(Calendar.MINUTE));
System.out.println("秒:"+calendar.get(Calendar.SECOND));

其中Calendar.getInstance()就使用了简单工厂模式:
在这里插入图片描述


7.工厂模式的意义

将实例化对象的代码提取出来,放在一个类中统一管理和维护,达到和主项目的依赖关系的解耦,从而提高项目的扩展和维护性。


8.设计模式的依赖抽象原则

  • 创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的人方法中返回。即变量不要直接持有具体类的引用。
  • 不要让类继承具体类,而是继承抽象类或者是实现interface。
  • 不要覆盖基类中已经实现的方法。