java设计模式:工厂方法模式(Factory Method)
工厂方法模式
前言:
《大话设计模式》里有一小节叫'活字印刷,面向对象'的,讲了一个小故事,大意如下:
话说三国时期,曹操带领大军驻扎于赤壁。军船相连,气势恢宏,眼看要灭掉东吴,统一天下,曹操甚悦,于是大宴群臣。席间曹操诗兴大发,不觉吟道:"喝酒唱歌,人生真爽。……"。众文武齐呼:"丞相好诗!"。于是一臣子速命印刷工匠刻板印刷,以便流传天下。
样章出来曹操一看,感觉不妥,道:"喝与唱此话过俗,应该为'对酒当歌'较好!",于是此臣就命工匠重新来过,此工匠连夜之功就此白费,只得重刻之。
样章再次出来请曹操过目,曹操细细一品,觉得还是不好,说:"人生真爽太过直接,应改问语才够意境,就改为'对酒当歌人生几何?……'吧"。当臣子转告工匠时,工匠晕倒。。。
这里的问题是三国时期活字印刷还未发明,所以改字的时候,就必须整块板全部重刻!如果有了活字印刷,则只需更改四个字即可,其余工作均未白做。
第一,要改只需更改要改之字,此为可维护;第二,这些字并非这次用完就无用,完全可以在后来的印刷中重复使用,此乃可复用;第三此诗若要加字,只需另刻字加入即可,这是可扩展;第四字的排列其实可能竖排可能横排,此时只需将活字移动即可满足排列需求,此是灵活性好。编写代码时多考虑封装,继承,多态把程序的耦合度降低,使用面向对象思想使得程序更加的灵活,容易修改,并且易于复用。
好了,说了这么多,相信大家对设计模式多少有点感觉了。不错,设计模式就是为了让我们的代码更灵活,易扩展,好维护和能复用。现在我们就先从工厂模式总结吧!
一、简单工厂模式
1.介绍
总结工厂模式之前先得说说简单工厂模式。简单工厂模式(又名静态工厂方法),不属于GOF的23种设计模式之一。这种模式将创建对象的责任交由一个工厂对象,该工厂对象提供静态公有方法向外提供生成对象的服务,所有生成对象的逻辑均包含在该静态方法里,对外不可见。简单工厂是工厂模式里最好理解的模式了,可以将其看作所有工厂模式的基础。
2.UML类图
假装这里有类图
3.参考代码
//这是客户端,
public class Client {
public static void main(String[] args) {
// 客户端只需要传入需要的产品类型,就能得到对应的产品,用户不需要知道创建产品的细节
Product product = SimpleFactory.create("A");
// Product product = SimpleFactory.create("B");
// 调用生成产品的显示价格方法
product.showPrice();
}
}
/**
* 抽象产品角色
*
* @author ICE_melt
*
*/
interface Product {
// 显示产品价格
public void showPrice();
}
// 具体产品A
class ConcreateProductA implements Product {
@Override
public void showPrice() {
System.out.println("这是A产品,价格为10RMB,很便宜");
}
}
// 具体产品B
class ConcreateProductB implements Product {
@Override
public void showPrice() {
System.out.println("这是B产品,价格为600RMB,很贵的");
}
}
/**
* 工厂角色(即普通类提供了创建其他类的方法)
*
* @author ICE_melt
*
*/
class SimpleFactory {
/**
* 简单工厂一般为静态方法,其内包含必要的逻辑判断,能够根据客户端的条件创建具体的产品对象
* <br><b>剖析:</b>
* <br>现在的产品都是基于<code>Product<code>接口的子类,如果增加产品,继承体系没有问题
* <br>但是工厂生成逻辑需要修改(增加if分支判断逻辑),这一点违背了“开放封闭原则”
*/
public static Product create(String type) {
if ("A".equals(type)) {
return new ConcreateProductA();
} else if ("B".equals(type)) {
return new ConcreateProductB();
} else {
new RuntimeException(type + "产品类型暂时不能生产,请联系生产厂商!");
}
return null;
}
}
4.总结
简单工厂模式的优点是分离了产品的创建者和使用者,有利于软件系统结构的优化。局限是有需求变动时不修改代码的话是不能够扩展的。
1.介绍
工厂方法模式,又叫做多态性工厂。该模式定义一个用于创建对象的接口,让子类决定实例化那一个产品对象。工厂方法使一个类的实例化延迟到其子类。
2.UML类图
假装有类图
3.参考代码
首先是抽象产品类:
/**
* 产品抽象
*
* @author ICE_melt
*
*/
public abstract class AbstractProduct {
public AbstractProduct() {
}
// 所有产品都有对外展示的方法
public abstract void display();
}
基础抽象产品类的三种产品:
//产品--汽车
public class CarProduct extends AbstractProduct {
@Override
public void display() {
System.out.println("我有四个轮子,在陆地上跑的飞快");
}
}
//产品--飞机
public class AirplaneProduct extends AbstractProduct {
@Override
public void display() {
System.out.println("我有两个机翼,能在天上飞");
}
}
//产品--轮船
public class ShipProduct extends AbstractProduct {
@Override
public void display() {
System.out.println("我在海里,速度很快");
}
}
然后是工厂接口:
/**
* 一个工厂接口,负责定义产品对象的生产
* (工厂接口也可以用抽象方法实现)
* @author ICE_melt
*
*/
public interface IFactory {
/*
*
* 创建一个产品对象
*/
public AbstractProduct produceProduct();
}
工厂接口的三个具体工厂,每个工厂负责生产一种产品:
//汽车工厂,生产汽车
public class CarFactory implements IFactory {
@Override
public AbstractProduct produceProduct() {
return new CarProduct();
}
}
//飞机工厂,生产飞机
public class AirplaneFactory implements IFactory {
@Override
public AbstractProduct produceProduct() {
return new AirplaneProduct();
}
}
//轮船工厂,生产轮船
public class ShipFactory implements IFactory {
@Override
public AbstractProduct produceProduct() {
return new ShipProduct();
}
}
客户端代码:
/**
* 客户端代码
* @author ICE_melt
*
*/
public class Client {
public static void main(String[] args) {
//这里可以将工厂类的全路径类名写到项目的配置文件里,并添加一个读取该配置的方法
String factoryConfig = readConfig();
IFactory factory;
try {
//客户端代码只与抽象工厂 和 抽象产品 这两个顶级接口耦合,
//后台增加 一种新的产品的话,只需要同时增加 该产品的工厂类,
//不需要修改任何已存在的代码
factory = (IFactory)Class.forName(factoryConfig).newInstance();
AbstractProduct product = factory.produceProduct();
product.display();
} catch (Exception e) {
//异常处理
e.printStackTrace();
}
}
public static String readConfig(){
//可以将工厂类名全路径写到配置文件里,这样客户端无需
//更改代码,只需配置即可完成不用产品的的展示需求
return "com.icemelt.designpattern.factorymethod.AirplaneFactory";
}
}
4.总结
工厂方法首先需要屏蔽产品类的具体实现,产品类如何变化,调用者都不需要关心,只需关心产品类的接口,通常接口是相对稳定,只要此接口不发生变化,那么系统上层就不会发生变化(这也是面向接口编程的好处)。其次需要屏蔽工厂方法的具体实现,道理同前。然后我们就能享受工厂方法给代码带来的灵活性的好处:此时所有客户端代码只与两个抽象类(或接口)有关,增加产品只需完成该产品和对应工厂方法的编码工作即可完成需求增加(不需要修改客户端代码和后台已存在的代码,客户需要新增功能只需修改配置即可)
通常实际应用中业务逻辑会比较复杂,产品类可能并没有统一的接口(也有可能是历史遗留原因),这时候需要想办法加上一层统一的间接关系后才能继续应用本模式。