本期小编继续带大家来了解设计模式,这次为大家带来比较实用的模式,创建型模式。创建型模式主要是为了将系统与它的对象创建、结合、表示的方式分离。这些设计模式在对象创建的类型、主体、方式、时间等方面提高了系统的灵活性。这个模式大家在实际业务场景中也是应用最多的,比方说spring ioc容器那个工厂(这个比较复杂),当然最为熟悉的就是工厂和单例模式。接下来小编为大家介绍一下工厂模式。
工厂模式:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。这边理解的话就是标准化构建对象。
工厂模式主要分三类:简单工厂模式,工厂方法模式以及抽象方法模式,下面小编会通过结构图,代码示例,在源码中的应用来一个个分别讲解。
public class SimpleFactory {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Phone apple = simpleFactory.createPhone("apple");
apple.sendMsg();
}
public Phone createPhone(String name) {
if ("apple".equals(name)) {
return new ApplePhone();
} else if ("xiaomi".equals(name)) {
return new XiaomiPhone();
} else {
throw new IllegalArgumentException("不支持类型手机");
}
}
interface Phone {
void sendMsg();
}
class ApplePhone implements Phone {
@Override
public void sendMsg() {
System.out.println("apple phone send msg");
}
}
class XiaomiPhone implements Phone {
@Override
public void sendMsg() {
System.out.println("xiao mi send msg");
}
}
}
运行结果
apple phone send msg
这边小编为了方便就用内部类方式写了,是不是觉得非常简单啊。
简单工厂在日志框架中运用居多,包括dubbo,mybatis等,这边咱们举一个dubbo的日志工厂来讲一下。小编将最精简的代码放到下面。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public class LoggerFactory {
private static volatile LoggerAdapter LOGGER_ADAPTER;
private static final ConcurrentMap<String, FailsafeLogger> LOGGERS = new ConcurrentHashMap();
private LoggerFactory() {
}
//使用简单工厂,是不是其实框架也写很多if else代码
static {
String logger = System.getProperty("dubbo.application.logger");
if ("slf4j".equals(logger)) {
setLoggerAdapter((LoggerAdapter)(new Slf4jLoggerAdapter()));
} else if ("jcl".equals(logger)) {
setLoggerAdapter((LoggerAdapter)(new JclLoggerAdapter()));
} else if ("log4j".equals(logger)) {
setLoggerAdapter((LoggerAdapter)(new Log4jLoggerAdapter()));
} else if ("jdk".equals(logger)) {
setLoggerAdapter((LoggerAdapter)(new JdkLoggerAdapter()));
} else {
try {
setLoggerAdapter((LoggerAdapter)(new Log4jLoggerAdapter()));
} catch (Throwable var6) {
try {
setLoggerAdapter((LoggerAdapter)(new Slf4jLoggerAdapter()));
} catch (Throwable var5) {
try {
setLoggerAdapter((LoggerAdapter)(new JclLoggerAdapter()));
} catch (Throwable var4) {
setLoggerAdapter((LoggerAdapter)(new JdkLoggerAdapter()));
}
}
}
}
}
}
除了日志工厂还包括mybatis中的执行器创建它在configuration类中,它并不是工厂命名方式,如下面代码,依旧if和else。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
当然上面两个例子中其实还用了其他设计模式。小伙伴想一下是哪些啊?
优点:
- 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
- 客户端无需知道所创建具体产品的类名,只需知道参数即可。
- 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
缺点:
- 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
- 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
- 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
由于简单工厂有上述缺点,所以在简单工厂的基础上,为了其扩展性,咱们引入了工厂方法模式,他其实是将工厂继续抽象出来。看下器结构图
public class MethodFactory {
public static void main(String[] args) {
//这个因为是自己测试,时间是通过配置文件读取而来的。
MyMethodFactory methodFactory = new MethodFactory().new AppleFactory();
methodFactory.createPhone().sendMsg();
}
interface MyMethodFactory{
public Phone createPhone();
}
class AppleFactory implements MyMethodFactory{
@Override
public Phone createPhone() {
System.out.println("apple phone factory");
return new ApplePhone();
}
}
class XiaomiFactory implements MyMethodFactory{
@Override
public Phone createPhone() {
System.out.println("xiao mi phone factory");
return new XiaomiPhone();
}
}
interface Phone {
void sendMsg();
}
class ApplePhone implements Phone {
@Override
public void sendMsg() {
System.out.println("apple phone send msg");
}
}
class XiaomiPhone implements Phone {
@Override
public void sendMsg() {
System.out.println("xiao mi send msg");
}
}
}
执行结果
apple phone factory
apple phone send msg
上面读取配置省略了,如果有兴趣可以点开参考文章中的链接。那边有配置文件读取示例。
工厂方法的工厂在源码中的咱们举一个dubbo的注册中心,dubbo注册中心有好多,最常用的是zookeeper,还有其他包括redis,multicast等等。咱们挑最重要的源码看一下啊
//工厂接口
@SPI("dubbo")
public interface RegistryFactory {
/**
* 连接注册中心.
*
* 连接注册中心需处理契约:
* 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。
* 2. 支持URL上的username:password权限认证。
* 3. 支持backup=10.20.153.10备选注册中心集群地址。
* 4. 支持file=registry.cache本地磁盘文件缓存。
* 5. 支持timeout=1000请求超时设置。
* 6. 支持session=60000会话超时或过期设置。
*
* @param url 注册中心地址,不允许为空
* @return 注册中心引用,总不返回空
*/
@Adaptive({
"protocol"})
Registry getRegistry(URL url);
}
//抽象工厂
public abstract class AbstractRegistryFactory implements RegistryFactory {
public Registry getRegistry(URL url) {
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
String key = url.toServiceString();
// 锁定注册中心获取过程,保证注册中心单一实例
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
//具体注册中心调到的方法
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);
return registry;
} finally {
// 释放锁
LOCK.unlock();
}
}
//具体工厂所需要实现的类
protected abstract Registry createRegistry(URL url);
}
//以zookeeper为例
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
private ZookeeperTransporter zookeeperTransporter;
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
}
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
- 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
- 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
- 类的个数容易过多,增加复杂度。
- 增加了系统的抽象性和理解难度。
- 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
讲到抽象工厂,首先上面工厂是一整套的,什么是一整套,就是比方说是手机,他生产出来就是一个手机。那假如说工厂是代加工的,屏幕是tcl或三星的,cpu是高通或天机的,摄像头是索尼的等等。然后不同品牌的手机需要的是一整个手机,需要的是将屏幕,cpu,摄像头等等拼装成的。那产品是不是工厂需要抽象生产一系列的产品,当然产品也是不同点。然后一系列的产品为一个品牌的手机。不知道小编讲清楚没有。接下来上模型结构图
这边小编简单的实现一个
工厂抽象
public interface PhoneFactory {
Cpu createCpu();
Camera createCamera();
Screen createScreen();
interface Cpu{
void getCpuType();
}
interface Camera{
void getCameraType();
}
interface Screen{
void getScreenType();
}
}
具体实现
public class AbstractFactory {
public static void main(String[] args) {
//跟工厂方法一样,这个根据配置获得。测试简单创建对象
PhoneFactory abstractFactory = new AbstractFactory().new XiaomiPhoneFactory();
abstractFactory.createCamera().getCameraType();
}
class XiaomiPhoneFactory implements PhoneFactory{
public XiaomiPhoneFactory(){
System.out.println("xiao mi phone factory");
}
@Override
public Cpu createCpu() {
return new XiaomiCpu();
}
@Override
public Camera createCamera() {
return new XiaomiCamera();
}
@Override
public Screen createScreen() {
return new XiaomiScreen();
}
}
class ApplePhoneFactory implements PhoneFactory{
@Override
public Cpu createCpu() {
return new AppleCpu();
}
@Override
public Camera createCamera() {
return new AppleCamera();
}
@Override
public Screen createScreen() {
return new AppleScreen();
}
}
class XiaomiCpu implements PhoneFactory.Cpu{
@Override
public void getCpuType() {
System.out.println("xiao mi Cpu");
}
}
class XiaomiScreen implements PhoneFactory.Screen{
@Override
public void getScreenType() {
System.out.println("xiao mi Screen");
}
}
class XiaomiCamera implements PhoneFactory.Camera{
@Override
public void getCameraType() {
System.out.println("xiao mi Camera");
}
}
class AppleCpu implements PhoneFactory.Cpu{
@Override
public void getCpuType() {
System.out.println("apple cpu");
}
}
class AppleScreen implements PhoneFactory.Screen{
@Override
public void getScreenType() {
System.out.println("apple Screen");
}
}
class AppleCamera implements PhoneFactory.Camera{
@Override
public void getCameraType() {
System.out.println("apple Camera");
}
}
}
暂时小编并未找到,可能对于一个大型平台中有应用此工厂模式但是没有开源的。
优点:
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
- 同时包含了上面工厂的优点
缺点:
- 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
讲到这儿,今天的工厂模式就已经结束了,不知道大家更喜欢用那种方式呢,小编觉得自己水平有限,用用简单的就行了,毕竟在咱们实际的业务中,已经够用,并且后来的小伙伴们看代码时能够更加清晰明了。小伙伴们觉得呢,希望给大家带来帮助啊。
感谢源码阅读网,鲁班大叔的讲解及案例
本文借鉴了相关文章
简单工厂 :http://c.biancheng.net/view/8385.html.
工厂方法: http://c.biancheng.net/view/1348.html.
抽象工厂: http://c.biancheng.net/view/1351.html.