设计模式之工厂方法模式

工厂方法模式
1 工厂方法模式的引入——简单工厂模式的不足
在《设计模式之简单工厂模式》一文中,提到简单工厂模式:
1)只提供了一个工厂类,该工厂类知道每一个产品对象的创建细节;
2)当有新产品要加入时,必须修改工厂类,加入必要的处理逻辑,这违背了“开闭原则”,也是其最大的缺点
3)工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,影响系统的灵活性和扩展性。
因此,引入工厂方法模式。
2 模式动机
  如果需要增加一种新类型的按钮,那么除了增加一个新的具体产品类之外,还需要修改工厂类的代码,这就使得整个设计在一定程度上违反了“开闭原则”。
  针对上述这一问题,定义一个抽象的按钮工厂类,
再定义具体的工厂类来生成圆形按钮、矩形按钮、菱形按钮等,它们实现在抽象按钮工厂类中定义的方法。因此,
可以在不修改具体工厂类的情况下引进新的产品,更加符合“开闭原则”。
3 模式意图

   工厂方法(Factory Method)模式,又称为虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式。

   其模式意图是定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
   具体体现为:工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象。即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
4 模式结构

产品(接口或抽象类):

定义工厂方法所创建对象的接口。

具体产品(类):实现产品接口。
生产者:声明工厂方法,该方法返回产品类型的对象。
具体生产者:Overrides工厂方法来返回一个具体产品的实例。
以在简单工厂模式中的实例来说明工厂方法模式的实现。
public abstract class PayMethodFactory{ //抽象工厂类
     public abstract AbstractPay getPayMethod();
} 
public class CashPayFactory extends PayMethodFactory{ //具体工厂类
    public AbstractPay getPayMethod({
         return new CashPay();
    }
} 
public abstract class AbstractPay{  //抽象产品类
   public abstract void pay();  
}
public class CashPay extends AbstractPay{  //具体产品类
   public void pay(){  
    //现金支付处理代码  
  }  
}
// 客户端
PayMethodFactory factory;
AbstractPay payMethod;
factory=new CashPayFactory();
payMethod =factory.getPayMethod();
payMethod.pay(); 
5 工厂方法模式的优点
1)在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无需关心创建细节,甚至无需知道具体产品类的类名
2)基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部工厂方法模式之所以又被称为多态工厂模式,正是因为所有的具体工厂类都具有同一抽象父类。
3)使用工厂方法模式的另一个优点是在系统中加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
工厂方法模式的缺点
1)在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
2)由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
6 模式应用场景
应用场景1

  将具体工厂类的实例化过程进行改进,不直接使用new关键字来创建对象,而是将具体类的类名写入配置文件中,再通过Java的反射机制,读取XML格式的配置文件,根据类名字符串生成对象。

下述代码段展示了一句类名字符串生成对象。

有如下XML配置文件config.xml:

<?xmlversion="1.0"?>
<config>
  <className>CashPayFactory</className>
</config>
//创建DOM文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dFactory.newDocumentBuilder();
			Document doc;							
			doc = builder.parse(new File("config.xml")); 
		
	   //获取包含类名的文本节点
	     NodeList nl = doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName=classNode.getNodeValue();
            
            //通过类名生成实例对象并将其返回
            Class c=Class.forName(cName);
	  	    Object obj=c.newInstance();
            return obj;

应用场景2
java.util.Collection接口的iterator()方法:

7 模式扩展
1)使用多个工厂方法在抽象工厂角色中可以定义多个工厂方法,从而使具体工厂角色实现这些不同的工厂方法,这些方法可以包含不同的业务逻辑,以满足对不同的产品对象的需求。
2)产品对象的重复使用工厂对象将已经创建过的产品保存到一个集合(如数组、List等)中,然后根据客户对产品的请求,对集合进行查询。如果有满足要求的产品对象,就直接将该产品返回客户端;如果集合中没有这样的产品对象,那么就创建一个新的满足要求的产品对象,然后将这个对象在增加到集合中,再返回给客户端。
3)多态性的丧失和模式的退化如果工厂仅仅返回一个具体产品对象,便违背了工厂方法的用意,发生退化,此时就不再是工厂方法模式了。一般来说,工厂对象应当有一个抽象的父类型,如果工厂等级结构中只有一个具体工厂类的话,抽象工厂就可以省略,也将发生了退化。当只有一个具体工厂,在具体工厂中可以创建所有的产品对象,并且工厂方法设计为静态方法时,工厂方法模式就退化成简单工厂模式。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值