你应该了解的工厂方法模式:优雅的代码永不过时(二)

简介: 你应该了解的工厂方法模式:优雅的代码永不过时

2、升级为多个工厂类


当我们在做一个比较复杂的项目时,经常会遇到初始化一个对象很耗费精力的情况所有的产品类都放到一个工厂方法中进行初始化会使代码结构很不清晰这里的初始化不仅仅是指代new,初始化包括new一个对象,并为对象设置一定的初始值)。例如:一个产品类具有

5个具体实现,每个类的初始化都不相同,如果写在一个工厂方法中,势必会导致该方法无比巨大,如果遇到这种情况该如何处理呢?

考虑到需要结构清晰,因此我们为每个产品定义一个创造者,然后由调用者自己去选择与哪个工厂方法关联。还是以女娲造人为例,每个

每个人种都有一个固定的八卦炉,分别造出黑色人种、白色人种、黄色人种。

修改后的类图为

image.png

image.png

每个人种(具体的产品类)都对应了一个创建者,每个创建者都独立负责创建对应的产品对象

程序代码

代码没有发生改变的程序

// 多工厂模式中的Human接口publicinterfaceHuman {
// 每个人种的皮肤都有相应的颜色publicvoidgetColor();
// 只要是人类就都是会说话的publicvoidtalk();
}
// 多工厂模式中的黄色人种类publicclassYellowHumanimplementsHuman {
@OverridepublicvoidgetColor() {
System.out.println("黄种人的肤色是黄色的");
    }
@Overridepublicvoidtalk() {
System.out.println("黄种人:古老的东方有一条龙");
    }
}
// 多工厂模式中的白色人种类publicclassWhiteHumanimplementsHuman {
@OverridepublicvoidgetColor() {
System.out.println("白种人的肤色肯定是白色的");
    }
@Overridepublicvoidtalk() {
System.out.println("白种人:有汰渍,没污渍!汰渍,专业漂白100年");
    }
}
// 多工厂模式中的黑色人种类publicclassBlackHumanimplementsHuman {
@OverridepublicvoidgetColor() {
System.out.println("黑色人种的肤色当然是黑色的");
    }
@Overridepublicvoidtalk() {
System.out.println("黑种人:我们为黑人牙膏代言");
    }
}

代码发生改变的程序

// 多工厂模式的抽象工厂类
public abstract class AbstractHumanFactory {
    public abstract Human createHuman();
 }

注意:抽象方法中已经不需要再传递相关的参数了因为每一个具体的工厂都已经安排非常明确自己的职责创建自己负责的产品类对象

// 多工厂模式中的黑色人种的创建工厂实现publicclassBlackHumanFactoryextendsAbstractHumanFactory {
@OverridepublicHumancreateHuman() {
returnnewBlackHuman();
    }
}


// 多工厂模式中的黄色人种的创建工厂实现publicclassYellowHumanFactoryextendsAbstractHumanFactory {
@OverridepublicHumancreateHuman() {
returnnewYellowHuman();
    }
}


// 多工厂模式中的白色人种的创建工厂实现publicclassWhiteHumanFactoryextendsAbstractHumanFactory {
@OverridepublicHumancreateHuman() {
returnnewWhiteHuman();
    }
}

以上三个具体的创建工厂都非常简单,但是如果一个系统比较复杂时工厂类也会相应对地变得复杂

// 多工厂模式中的场景类——女娲类publicclassNvWa {
publicstaticvoidmain(String[] args) {
// 女娲第一次造人,火候不足,于是白色人种产生了System.out.println("<---- 造出的第一批人是白种人 ---->");
// 发生变化的代码HumanwhiteHuman= (newWhiteHumanFactory()).createHuman();
System.out.println("-- 获取白色人种的肤色 --");
whiteHuman.getColor();
System.out.println("-- 让我们听听白色人种想说什么 --");
whiteHuman.talk();
// 女娲第二次开始造人,烧制时间过长出现了黑人System.out.println("<---- 造出的第二批人是黑种人 ---->");
// 发生变化的代码HumanblackHuman= (newBlackHumanFactory()).createHuman();
System.out.println("-- 获取黑色人种的肤色 --");
blackHuman.getColor();
System.out.println("-- 让我们听听黑色人种想说什么 --");
blackHuman.talk();
// 女娲第三次造人吸取了前两次的教训,这次烧制成功了System.out.println("<---- 造出的第三批人是黄种人 ---->");
// 发生变化的代码HumanyellowHuman= (newYellowHumanFactory()).createHuman();
System.out.println("-- 获取黄色人种的肤色 --");
yellowHuman.getColor();
System.out.println("-- 让我们听听黄色人种想说什么 --");
yellowHuman.talk();
    }
}

运行结果

image.png

可以发现运行结果与之前的结果仍然相同。每一个产品类都对应了一个创建类,好处就是创建类的职责清晰,而且结构简单,但是给可扩展性和可维护性带来了一定的影响。不妨我们这样想一想,如果要扩展一个产品类,就需要建立一个相应的工厂类,这样就增加了扩展的难度。因为工厂类和产品类的数量相同你那个,维护时就需要考虑到两个对象间的关系

在比较复杂的应用中一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,修调类的作用是封装子工厂类,对高层模块提供统一的访问接口

3、替代单例模式


单例模式的核心要求就是在内存中只有一个对象通过工厂方法模式也可以只在内存中生产一个对象

类图

image.png

代码实现

单例类

publicclassSingleton {
// 不允许通过new产生一个对象privateSingleton(){
    }
publicvoiddoSomething(){
// 业务处理    }
}

Singleton保证不能通过正常的渠道建立一个对象,那SingletonFactory生产单例类的对象可以通过反射来创建

生产单例的工厂类

publicclassSingletonFactory {
privatestaticSingletonsingleton;
static {
try {
Classc=Class.forName(Singleton.class.getName());
// 获得无参构造Constructorconstructor=c.getDeclaredConstructor();
// 开启private访问权限,设置无参构造是可以访问的constructor.setAccessible(true);
// 产生一个实例对象singleton= (Singleton)constructor.newInstance();
        } catch (Exceptione) {
// 异常处理        }
    }
}

通过获得类构造器,然后设置访问权限,生成一个对象,接着提供外部访问,保证内存中的对象唯一。以上通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在一个项目中可以产生一个单例构造器,只要满足所有产生单例的类的构造方法都是private类型的就可以通过扩展该框架,只要输入一个类型就可以获得唯一的一个实例

4、延迟初始化


延迟初始化:

一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。

image.png

类图解析

  1. ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重复使用的对象保留
  2. Product和ConcreteProduct与之前的通用代码中的抽象产品类和具体产品类一致,负责定义产品的共性,实现对事物最抽象的定义定义该类型产品的具体内容

代码实现

抽象产品类

publicabstractclassProduct1 {
// 产品类的公共方法publicvoidmethod(){
// 具体的业务处理    }
// 抽象方法publicabstractvoidmethod1();
}
publicabstractclassProduct2 {
// 产品类的公共方法publicvoidmethod(){
// 具体的业务处理    }
// 抽象方法publicabstractvoidmethod2();
}

具体产品类

publicclassConcreteProduct1extendsProduct1 {
@Overridepublicvoidmethod1() {
// 具体的业务处理    }
}
publicclassConcreteProduct2extendsProduct2 {
@Overridepublicvoidmethod2() {
// 具体的业务处理    }
}

延迟加载工厂类

publicclassProductFactory {
privatestaticfinalMap<String, Product>prMap=newHashMap<>();
publicstaticsynchronizedProductcreateProduct(Stringtype) throwsException{
Productproduct=null;
// 如果Map中已经有这个对象if (prMap.containsKey(type)){
product=prMap.get(type);
        } else {
if (type.equals("Product1")){
product=newConcreteProduct1();
            } else {
product=newConcreteProduct2();
            }
// 同时把对象放到缓存容器中prMap.put(type,product);
        }
returnproduct;
    }
}

通过定义一个Map容器,容纳所有产生的对象如果在Map容器中已经有的对象,则直接取出返回;如果没有,则根据需要的类型产生一个对象并放入Map容器中,以方便下次调用

如果限制某一个产品类最大实例化的数量,可以通过判断 Map中已有的对象的数量来实现,这样处理是非常有意义的,例如JDBCl连接数据库,都会要求设置MaxConnections最大连接数量,该数量就是内存中最大实例化的数量。延迟加载还可以使用在对象初始化比较复杂的情况下,例如硬件的访问,设计多方面的交互时可以通过延迟加载来降低对象的产生和销毁带来的复杂性。

总结


以上便是Java设计模式中的工厂方法模式,工厂方法模式在项目中使用的非常频繁,以至于很多代码中都包含工厂方法模式。在学习的过程中感觉工厂方法模式可以很好的做到与其它模式混合使用,变化出无穷的优秀设计,可能这也正是软件设计和开发的乐趣所在。

其它设计模式传送门:


UMLl类图: 为掌握Java设计模式奠基—两步搞懂UML类图

单例模式: 关于单例模式,你应该了解这些

相关文章
|
设计模式 前端开发 Java
意外发现,原来你不知道自己每天都在用门面模式
一般的电商平台都是整合众多的子系统聚合到一起形成一个大型的购物平台,一般情况下,有很多现成的功能都不是重新开发的,而是要去对接已有的各个子系统,这些子系统可能涉及积分系统、支付系统、物流系统的接口调用。如果所有的接口调用全部由前端发送网络请求去调用现有接口,一则会增加前端开发人员的难度,二则会增加一些网络请求,影响页面性能。此时就可以发挥门面模式的优势了。将所有现成的接口全部整合到一个类中,由后端提供统一的接口供前端调用,这样前端开发人员就不需要关心各接口的业务关系,只需要把精力集中在页面交互上。我们用代码来模拟一个积分兑换礼品的业务场景。
98 0
意外发现,原来你不知道自己每天都在用门面模式
|
4月前
软件复用问题之复用性失效的原因是什么,如何解决
软件复用问题之复用性失效的原因是什么,如何解决
|
6月前
|
设计模式 缓存 安全
单例模式的应用与陷阱:确保你的代码只有一个实例
单例模式的应用与陷阱:确保你的代码只有一个实例
182 0
|
6月前
|
设计模式 存储
二十三种设计模式全面解析-深入探究备忘录模式:保留过去,预见未来
二十三种设计模式全面解析-深入探究备忘录模式:保留过去,预见未来
|
设计模式 网络协议 Java
经典永不过时!重温设计模式
在软工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领域引入到计算机科学的,设计模式是针对软件设计中常见问题的工具箱,其中的工具就是各种经过实践验证的解决方案。即使你从未遇到过这些问题,了解模式仍然非常件有用,因为它能指导你如何使用面向对象的设计原则来解决各种问题。
191 1
经典永不过时!重温设计模式
|
设计模式 Java
【Java设计模式 规范与重构】 一 重构的目的、内容、时机、方法
【Java设计模式 规范与重构】 一 重构的目的、内容、时机、方法
187 0
|
安全 Java 测试技术
工作中单例模式用法及其使用场景?
工作中单例模式用法及其使用场景?
97 0
关于《生成器运行时机导致的难以察觉的 bug》勘误
关于《生成器运行时机导致的难以察觉的 bug》勘误
76 0
|
存储 算法 安全
万字 HashMap 详解,基础(优雅)永不过时
在上一篇文章里,我们聊到了散列表的整体设计思想,在后续几篇文章里,我们将以 Java 语言为例,分析标准库中实现的散列表实现,包括 HashMap、ThreadLocalMap、LinkedHashMap 和 ConcurrentHashMap。今天,我们来讨论 Java 标准库中非常典型的散列表结构,也是 “面试八股
150 0
万字 HashMap 详解,基础(优雅)永不过时
|
设计模式 Java Go
你应该了解的工厂方法模式:优雅的代码永不过时(一)
你应该了解的工厂方法模式:优雅的代码永不过时
你应该了解的工厂方法模式:优雅的代码永不过时(一)