工厂方法格局的概念为:
《设计格局之禅》读书笔记(四)之抽象工厂情势
Define an interface for creating an object,but let subclasses decide which class toinstantiate.Factory Method lets a class defer instantiation to subclasses.(定义四个用以创建对象的接口,让子类决定实例化哪三个类。工厂方法使二个类的实例化延迟到其子类。)
一、抽象工厂格局的定义
Provide an Interface for creating families of related or dependent objects without specifying their concrete classes.(为开创一组有关或互相信任的指标提供三个接口,而且不需求点名他们的具体类。)
虚幻工厂格局的通用类图如下:
虚幻工厂方式是工厂方法方式的提高版本,在有多个职业体系、业务分类时,通过架空工厂方式发生须要的靶牛时一种十一分好的消除情势。
在空洞工厂方式中,有三个产品族的定义:所谓的产品族,是指身处分化出品等级结构中功效相关联的产品结合的家族。抽象工厂方式所提供的一体系产品就整合三个产品族;而工厂方法提供的一类别产品称为三个品级结构。例如创造小车的左侧门和左侧门,这些的数码应该是特别的——那是多个对象时期的封锁,每种型号的车门都以不相同的,这是成品等第结构约束的。源码完成如下:
厂子方法形式的通用类图如下所示:
1. 虚幻产品类
厂子方法方式
1.1 产品A的悬空产品类:
package definationAbstractFactoryPattern;
/**
* @Title: AbstractProductA
* @description: 抽象产品实现类A
* @author: gaoyakang
* @date: 2017年11月19日 下午8:52:58
*
*/
public abstract class AbstractProductA {
//每个产品共有的方法
public void shareMethod() {
}
//每个产品相同方法,不同实现
public abstract void doSomething();
}
从图中能够看看,在工厂方法方式中,抽象产品类Product肩负定义产品的共性,完毕对事物最抽象的定义;Creator为架空成立类,也便是抽象工厂,具体什么创造产品类是由具体的落实工厂ConcreteCreator来变成的。
1.2 产品B的架空产品类:
package definationAbstractFactoryPattern;
/**
* @Title: AbstractProductB
* @description: 抽象产品实现类B
* @author: gaoyakang
* @date: 2017年11月19日 下午8:59:08
*
*/
public abstract class AbstractProductB {
//每个产品共有的方法
public void shareMethod() {
}
//每个产品相同方法,不同实现
public abstract void doSomething();
}
上边是二个比较通用的工厂方法格局的源码:
2. 产品完毕类
package definationAbstractFactoryPattern;
/**
* @Title: ProductA1
* @description: 产品A1的实现类
* @author: gaoyakang
* @date: 2017年11月19日 下午8:54:37
*
*/
public class ProductA1 extends AbstractProductA{
@Override
public void doSomething() {
System.out.println("产品A1的实现方法");
}
}
package definationAbstractFactoryPattern;
/**
* @Title: ProductA2
* @description: 产品A2的实现类
* @author: gaoyakang
* @date: 2017年11月19日 下午8:55:49
*
*/
public class ProductA2 extends AbstractProductA{
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("产品A2的实现方法");
}
}
package definationAbstractFactoryPattern;
/**
* @Title: ProductB1
* @description: 产品B1的实现类
* @author: gaoyakang
* @date: 2017年11月19日 下午8:59:48
*
*/
public class ProductB1 extends AbstractProductB{
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("产品B1的实现方法");
}
}
package definationAbstractFactoryPattern;
/**
* @Title: ProductB2
* @description: 产品B2的实现类
* @author: gaoyakang
* @date: 2017年11月19日 下午8:59:48
*
*/
public class ProductB2 extends AbstractProductB{
/* (non-Javadoc)
* @see definationAbstractFactoryPattern.AbstractProductB#doSomething()
*/
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("产品B1的实现方法");
}
}
空洞产品类:
3. 架空工厂类
有N个产品族,在空洞工厂类中就相应有N个创设方法
package definationAbstractFactoryPattern;
/**
* @Title: AbstractCreator
* @description: 抽象工厂类
* @author: gaoyakang
* @date: 2017年11月19日 下午8:57:12
*
*/
public abstract class AbstractCreator {
//创建A产品家族
public abstract AbstractProductA createProductA();
//创建B产品家族
public abstract AbstractProductB createProductB();
}
public abstract class Product { public void method1() { //产品类的公共方法 } public abstract void method2(); //抽象的方法}
4. 出品的落到实处类
始建产品是由现实的落到实处类来完结的
具体产品类:
4.1 产品等级1的兑现类
package definationAbstractFactoryPattern;
/**
* @Title: Creator1
* @description: 生产产品等级1的产品
* @author: gaoyakang
* @date: 2017年11月19日 下午9:04:09
*
*/
public class Creator1 extends AbstractCreator{
@Override
public AbstractProductA createProductA() {
//只生产产品等级为1的A产品
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
//只生产产品等级为1的B产品
return new ProductB1();
}
}
public class ConcreteProduct1 extends Product { //产品1 public void method2() { //具体代码 }}public class ConcreteProduct2 extends Product { //产品2 public void method2 { //具体代码 }}......
4.2 产品等第2的兑现类
package definationAbstractFactoryPattern;
/**
* @Title: Creator2
* @description: 生产产品等级2的产品
* @author: gaoyakang
* @date: 2017年11月19日 下午9:06:03
*
*/
public class Creator2 extends AbstractCreator{
@Override
public AbstractProductA createProductA() {
//只生产产品等级为2的A产品
return new ProductA2();
}
@Override
public AbstractProductB createProductB() {
//只生产产品等级为2的B产品
return new ProductB2();
}
}
切切实实的产品类能够是有四个,都以继续于肤浅产品类的,何况供给自身来贯彻抽象产品类中的抽象方法。
5. 场景类
package definationAbstractFactoryPattern;
/**
* @Title: Client
* @description: 场景类
* @author: gaoyakang
* @date: 2017年11月19日 下午9:08:45
*
*/
public class Client {
public static void main(String[] args) {
//定义出两个工厂
AbstractCreator creator1=new Creator1();
AbstractCreator creator2=new Creator2();
//产生A1对象
AbstractProductA a1=creator1.createProductA();
//产生A2对象
AbstractProductA a2=creator2.createProductA();
//产生B1对象
AbstractProductB b1=creator1.createProductB();
//产品B2对象
AbstractProductB b2=creator2.createProductB();
a1.doSomething();
a2.doSomething();
b1.doSomething();
b2.doSomething();
}
}
空洞工厂类:
二、抽象工厂方式的使用
public abstract class Creator {/*创建一个产品对象,其输入参数类型可以自己设置通常为String、Enum、Class等,也可以为空*/ public abstract <T extends Product> T createProduct(Class<T> c);}
空洞工厂情势的亮点
- 封装性:不用关注对象是什么样成立出来的,而只需求关心接口的。
- 产品族内的约束为非公开状态:例如男女比例为1.2:1,那么那一个涉及如若在工厂类中达成就足以了,而高层模块调用时不用关爱那么些约束。
空洞工厂类担当定义产品对象的发生,在此地我们利用了泛型,通过定义泛型对输入的参数产生两层限制:1)输入参数必需是Class;2)输入参数必需是Product的达成类。
虚幻工厂方式的毛病
最大的老毛病是产品族的恢宏拾贰分困难,比方笔者索要充实叁个成品C,那么首先作者要求扩充三个浮泛产品类AbstractProductC,其次增添产品C各阶段的兑现类,最终AbstractCreator还供给扩大一个办法createProductC(),并且多个产品品级的达成类也供给有相应的修改,那沉痛影响了开闭原则。
切实工厂类:
泛泛工厂方式的应用情状
贰个对象族或是一组未有另外涉及的对象都有同样的封锁,则能够应用抽象工厂情势。
public class ConcreteCreator extends Creator { public <T extends Product> T createProduct(Class<T> c) { Product product = null; try { product = Class.forName(c.getName.newInstance(); }catch(Exception e) { //异常处理 } return product; }}
空泛工厂情势的注意事项
虚幻工厂格局的欠缺是产品族的恢弘困难,并不是产品品级的扩展困难。该方式下,产品等级是轻易扩张的,扩大贰个产品品级,只要扩展八个工厂类担任新添出来的制品生产职责就可以。在只扩大产品等第那或多或少上看,抽象工厂方式是符合开闭原则的。
场景类的调用方法:
三、书中女阴造人事例源码
轻松易行,这一个事例是制作创建二种肤色的人,且人有儿女之分。
public class Client { public static void main(String[] args){ Creator creator = new ConcreteCreator(); Product product = new creator.createProduct(ConcreteProduct1.class); //更多处理 }}
1. 人种接口
/**
* @Title: Human
* @description: 人种接口
* @author: gaoyakang
* @date: 2017年11月13日 上午7:21:02
*
*/
public interface Human {
//每个人有相应的颜色
public void getColor();
//每个人都会说话
public void talk();
//每个人都有性别
public void getSex();
}
上边来看一个切实可行的事例,那几个事例代表是的女阴造人的历程,在那一个历程当中有四个指标:女希氏、八卦炉和二种肤色的人。在那边,我们得以将阴皇用场景类来代表,八卦炉类似于二个工厂来负担塑造人类,而两种分化肤色的人都以同三个接口下分裂的落实类。因而大家能够赢得下边包车型客车类图:
2. 反革命人种
/**
* @Title: AbstractWhiteHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月13日 上午7:22:33
*
*/
public abstract class AbstractWhiteHuman implements Human{
public void getColor() {
System.out.println("白色人种的皮肤都是白色的!");
}
public void talk() {
System.out.println("白色人种会说话,一般都是单字节!");
}
}
事例类图
3. 灰绿人种
/**
* @Title: AbstractBlackHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月13日 上午7:24:57
*
*/
public abstract class AbstractBlackHuman implements Human {
@Override
public void getColor() {
System.out.println("黑色人种的皮肤都是黑色的!");
}
@Override
public void talk() {
System.out.println("黑人会说话,一般人听不懂。");
}
}
在图中AbstractHumanFactory是多个抽象类,定义了三个八卦炉具有的完全作用,HumanFactory为它的完成类来完结成立人类的做事;Human是全人类的接口;NvWa则是一个场景类来担负实行有关的职务。具体代码如下:
4. 色爱人种
/**
* @Title: AbstractYellowHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午6:58:18
*
*/
public abstract class AbstractYellowHuman implements Human{
@Override
public void getColor() {
System.out.println("黄色人种的皮肤是黄色的!");
}
@Override
public void talk() {
System.out.println("黄色人种会说话,一般说的是双音节。");
}
}
//Human接口public interface Human { //人类的总称 public void getColor(); //肤色 public void talk(); //语言}
//黑色人种public class BlackHuman implements Human { public void getColor() { System.out.println("黑人肤色是黑色"); } public void talk() { System.out.println("黑人说话听不懂"); }}
//白色人种public class WhiteHuman implements Human { public void getColor() { System.out.println("白人的肤色是白色"); } public void talk() { System.out.println("白人说话是单音节"); }}
//黄色人种public class YellowHuman implements Human { public void getColor() { System.out.println("黄人的肤色是黄色的"); } public void talk() { System.out.println("黄人说话是双音节"); }}
//抽象的八卦炉public abstract class AbstractHumanFactory { public abstract <T extends Human> T createHuman(Class<T> c); }
//八卦炉的具体实现类public class HumanFactory extends AbstractHumanFactory { public <T extends Human> T createHuman(Class<T> c) { Human human = null; try { human = Class.forName(c.getName.newInstance(); //产生一个人种 }catch(Exception e) { e.printStackTrace(); } return human; }}
//场景类NvWapublic class NvWa { public static void main(String[] args) { AbstractHumanFactory YinYangLu = new HumanFactory(); //阴阳炉 System.out.println("第一批是白色人种"); Human whiteHuman = YinYangLu.createHuman(WhiteHuman.class); whiteHuman.getColor(); whiteHuman.talk(); System.out.println("第二批是黑色人种"); Human blackHuman = YinYangLu.createHuman(BlackHuman.class); blackHuman.getColor(); blackHuman.talk(); System.out.println("第三批是黄色人种"); Human yellowHuman = YinYangLu.createHuman(YellowHuman.class); yellowHuman.getColor(); yellowHuman.talk(); }}
5. 各类肤色人种差别性的达成类
因为有二种肤色二种性别所以一共有6个落到实处类
运行结果:
5.1 浅丁香紫女性人种
/**
* @Title: FamaleYellowHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午7:04:43
*
*/
public class FemaleYellowHuman extends AbstractYellowHuman{
@Override
public void getSex() {
System.out.println("黄人女性");
}
}
率先批人是反摄人心魄种黄种人肤色是反革命的黄种人说话是单音节第二批人是铜绿人种黄种人肤色是浅暗绛红的白种人说话听不懂第三批人是山榄白人种黄种人肤色是浅日光黄的黄人说话是双音节
5.2 蓝灰男性人种
/**
* @Title: MaleYellowHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午7:05:36
*
*/
public class MaleYellowHuman extends AbstractYellowHuman{
@Override
public void getSex() {
System.out.println("黄人男性");
}
}
工厂方法格局的优点重要有以下几点:具备出色的封装性,代码的逻辑清晰。在创制对象时毫不通晓创立对象的历程,只要驾驭那一个产品的类名就足以了。工厂方法格局的扩充性很精美。在扩张产品类的状态下,只要适度地修改具体的工厂类或增加学一年级个厂子类,就足以做到相应的事务逻辑。可以屏蔽产品类。即产品类的兑现如何变迁,调用者都无需关心,它只必要关爱产品的接口,只要接口保持不改变,系统中的上层模块就毫无发生变化,因为产品类的实例化职业是由工厂类肩负的,八个产品对象实际由哪三个出品生成是由工厂类决定的。
5.3 银灰女人人种
/**
* @Title: FemaleWhiteHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午7:07:49
*
*/
public class FemaleWhiteHuman extends AbstractWhiteHuman{
@Override
public void getSex() {
// TODO Auto-generated method stub
System.out.println("白人女性");
}
}
综上所述以上优点,大家能够总计出工厂方法形式的多少个利用情形:因为工厂方法格局是new三个对象的代替品,所以在装有须求改动对象的地方都得以利用,可是须要审慎地思索是否要增添四个厂子类举行田间管理,扩张代码的复杂度。当必要灵活的、可扩充的框架时,能够思虑选择工厂方法形式。举例供给规划二个老是邮件服务器的框架,有二种互连网契约可供选拔:POP3、IMAP、HTTP,我们就能够把那二种连接方式作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,用不一致的法门完毕四个有血有肉的产品类,之后再定义一个厂子方法,遵照区别的传入条件,选择不一样的总是情势。工厂方法方式可以用在异构项目中,比如通过WebService与三个非Java的类型互相,就算WebService可以称作是足以完结异构系统的同构化,可是在骨子里的花费中,依然会遇到非常多难题,如类型难题、WSDL文件的帮助难点,等等。从WSDL中生出的指标都为是二个出品,然后由三个切实的工厂类实行管理,裁减与外边系统的耦合。能够动用在测量检验驱动开辟的框架下。比方,测量检验四个类A,就要求把与类A有关系关系的类B也同期发生出来,我们得以应用工厂方法方式把类B设想出来,防止类A与类B的耦合。
5.4 水绿男子人种
/**
* @Title: MaleWhiteHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午7:08:24
*
*/
public class MaleWhiteHuman extends AbstractWhiteHuman{
@Override
public void getSex() {
System.out.println("白人男性");
}
}
厂子方法形式首要有以下4种扩大,下边来分别介绍。
5.5 水晶绿女人人种
/**
* @Title: FemaleBlackHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午7:06:40
*
*/
public class FemaleBlackHuman extends AbstractBlackHuman{
@Override
public void getSex() {
// TODO Auto-generated method stub
System.out.println("黑人女性");
}
}
当八个模块仅仅只须求贰个工厂类的时候,就从未有过须要把它独立生产出来,使用静态的不二等秘书技就可以了,比如神女的百般例子中AbstarctHumanFactory,大家得以将它来修改一下,将AbstarctHumanFactory直接去掉,相同的时间把createHuman方法设置为静态类型,代码如下:
5.6 灰绿男人人种
/**
* @Title: MaleBlackHuman
* @description:
* @author: gaoyakang
* @date: 2017年11月15日 上午7:07:16
*
*/
public class MaleBlackHuman extends AbstractBlackHuman{
@Override
public void getSex() {
System.out.println("黑人男性");
}
}
public class HumanFactory { public static <T extends Human> T createHuman(Class<T> c) { Human human = null; try { human = Class.forName(c.getName.newInstance(); //产生一个人种 }catch(Exception e) { e.printStackTrace(); } return human; }}
6. 八卦炉的定义
/**
* @Title: HumanFactory
* @description:
* @author: gaoyakang
* @date: 2017年11月16日 下午9:13:48
*
*/
public interface HumanFactory {
//制造一个黄色人种
public Human createYellowHuman();
//制造一个白色人种
public Human createWhiteHuman();
//制造一个黑色人种
public Human createBlackHuman();
}
接下来大家再将场景类NvWa中的代码改动一下:
7. 生产女性的八卦炉
/**
* @Title: FemaleFactory
* @description:
* @author: gaoyakang
* @date: 2017年11月16日 下午9:15:22
*
*/
public class FemaleFactory implements HumanFactory{
@Override
public Human createYellowHuman() {
return new FemaleYellowHuman();
}
@Override
public Human createWhiteHuman() {
return new FemaleWhiteHuman();
}
@Override
public Human createBlackHuman() {
return new FemaleBlackHuman();
}
}
public class NvWa { public static void main(String[] args) { System.out.println("第一批是白色人种"); Human whiteHuman = createHuman(WhiteHuman.class); whiteHuman.getColor(); whiteHuman.talk(); System.out.println("第二批是黑色人种"); Human blackHuman = createHuman(BlackHuman.class); blackHuman.getColor(); blackHuman.talk(); System.out.println("第三批是黄色人种"); Human yellowHuman = createHuman(YellowHuman.class); yellowHuman.getColor(); yellowHuman.talk(); }}
8. 生产男子的八卦炉
/**
* @Title: MaleFactory
* @description:
* @author: gaoyakang
* @date: 2017年11月16日 下午10:02:02
*
*/
public class MaleFactory implements HumanFactory{
@Override
public Human createYellowHuman() {
return new MaleYellowHuman();
}
@Override
public Human createWhiteHuman() {
return new MaleWhiteHuman();
}
@Override
public Human createBlackHuman() {
return new MaleBlackHuman();
}
}
这样重复运行咱们得以博得一致的结果,可是代码能够变得更加的简便易行。这种情势是工厂方法形式的削弱,因为简单,所以称它为简易工厂方式。
9. 具体场景类(阴皇造人)
/**
* @Title: NvWa
* @description:
* @author: gaoyakang
* @date: 2017年11月16日 下午10:04:11
*
*/
public class NvWa {
public static void main(String[] args) {
//第一条生产线,男性生产线
HumanFactory maleHumanFactory=new MaleFactory();
//第二条生产线,女性生产线
HumanFactory feMaleHumanFactory=new MaleFactory();
Human maleYellowHuman=maleHumanFactory.createYellowHuman();
maleYellowHuman.getColor();
maleYellowHuman.getSex();
maleYellowHuman.talk();
Human femaleYellowHuman=feMaleHumanFactory.createYellowHuman();
femaleYellowHuman.getColor();
femaleYellowHuman.getSex();
femaleYellowHuman.talk();
}
}
当大家在做七个相比复杂的类型时,只怕会遇见很难创立叁个目的的场所,全数的产品类都放置二个工厂方法个中实行开端化会使得代码的逻辑非常不够清楚,那时大家就能够为每二个出品概念贰个厂子。依旧以风皇为例,大家让每多少个种族都有生育本身的八卦炉。代码如下:
//抽象的工厂类public abstract class AbstractHumanFactory { public abstract Human createHuman(); }
//生产黑人的炉子public class BlackHumanFactory extends AbstractHumanFactory { public Human createHuman() { return new BlackHuman(); }}
//生产黄人的炉子public class YellowHumanFactory extends AbstractHumanFactory { public Human createHuman() { return new YellowHuman(); }}
//生产白人的炉子public class WhiteHumanFactory extends AbstractHumanFactory { public Human createHuman() { return new WhiteHuman(); }}
接下来在场景类中对代码实行改造就可以:
public class NvWa { public static void main(String[] args) { System.out.println("第一批是白色人种"); Human whiteHuman = (new WhiteHumanFactory.createHuman(); whiteHuman.getColor(); whiteHuman.talk(); System.out.println("第二批是黑色人种"); Human blackHuman = (new BlackHumanFactory.createHuman(); blackHuman.getColor(); blackHuman.talk(); System.out.println("第三批是黄色人种"); Human yellowHuman = (new YellowHumanFactory.createHuman(); yellowHuman.getColor(); yellowHuman.talk(); }}
运维一下,结果照旧同样的。像这么对每贰个产品类都创立了独家对应的工厂,好处便是种种工厂的天任务明並且协会轻便,不过却给可拓宽性和可维护性带来了自然的震慑,因为只要要新建几个产品类,就要树立一个一见如旧的工厂类,扩展了复杂度。
单例方式的宗旨须求是在内部存款和储蓄器中只可以够有贰个对象,大家透过工厂方法方式也足以兑现只在内部存款和储蓄器中生产几个指标。
类图
Singleton定义了八个private的无参构造函数,目标是不容许通过new的的不二等秘书诀创立三个指标,它的代码如下:
public class Singleton{ private Singleton(){} public void dosomething(){ //代码 }}
Singleton保险不能经过正规的水道创设二个对象,而SingletonFactory则是因此反射的主意去创设,代码如下:
public class SingletonFactory { private static Singleton singleton; static{ try { Class cl= Class.forName(Singleton.class.getName; //获得无参构造 Constructor constructor=cl.getDeclaredConstructor(); //设置无参构造是可访问的 constructor.setAccessible; //产生一个实例对象 singleton = (Singleton)constructor.newInstance(); } catch (Exception e) { //异常处理 } } public static Singleton getSingleton(){ return singleton; }}
本条点子是由此获得类构造器,然后设置访谈权限,生成八个对象,然后提供外界访谈,保障内部存款和储蓄器中的靶子独一。
当一个目的被开销完后,并不马上释放,工厂类保持其开始状态等待注重新被选取,延迟开始化是工厂方法方式的一个扩充应用,它的类图如下:
类图
ProductFactory负担产品类对象的创导职业,並且经过prMap变量发生叁个缓存,对须求再一次被援引的指标保留,Product和ConcreteProduc则t是七个演示代码,ProductFactory代码如下:
public class ProductFactory{ private static final Map<String,Product> prMap = new HashMap(); public static synchronized Product createProduct(String type) throws Exception{ Product product = null; if(prMap.containsKey{ //如果Map中有了这个对象 product = prMap.get; } else{ if(type.equals("Product1")) { product = new ConcreteProduct1(); } else{ product = new ConcreteProduct2(); } prMap.put(type,product); //把对象放到缓存器里面 } return product; }}
此地大家定义了壹个Map容器来包容发生负有发生的靶子,借使Map容器中一度有了对象,则一直抽出重回;若无,则基于须求的连串爆发二个对象并放入到Map容器中,以谋福下一遍的调用。
编辑:编程技术 本文来源:设计形式,设计情势之禅
关键词: