工厂模式
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。
- 不要覆盖基类中已经实现的方法。