暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

鲸技术:设计模式之工厂和单例模式

中航鲸技术 2019-05-15
419


设计模式由来


在1994年,由Erich Gamma、RichardHelm、Ralph Johnson 和 JohnVlissides四人合著出版了一本名为Design Patterns - Elements of Reusable Object-OrientedSoftware(中文译名:设计模式 - 可复用的面向对象软件元素)的书,该书首次提到了软件开发中设计模式的概念。

他们所提出的设计模式主要是基于以下的面向对象设计原则:
1. 接口编程而不是对实现编程。
2. 优先使用对象组合而不是继承。

Desing Pattern是一套被反复使用的、多数人知晓的、经过分类编辑的代码设计经验的总结。使用设计模式是为了可重用代码,让代码更容易被他人理解并保证代码的可靠性。


设计模式分类

      

设计模式可分为三种类型:

创建型:
用于描述如何创建对象。包含六个设计模式,分别是:单例模式、工厂模式、抽象工厂模式、原型模式和建造者模式。

结构型:
用于描述如何实现类或对象的组合。包含七个设计模式,分别是:适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式。

行为型:
用于描述类或对象怎样交互以及怎样分配职责。包含十一个设计模式,分别是:职责链模式、命令模式、解释器模式、迭代者模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。


设计原则

      

为什么要提倡DesignPattern呢?根本原因是为了代码复用,增加可维护性。那么怎样才能实现代码复用呢?

面向对象有以下五个原则:

开闭原则:
模块应对扩展开放,而对修改关闭。

里氏代换原则:
如果调用的是父类的话,那么换成子类也完全可以运行。可以说:里氏代换原则是继承复用的一个基础。

依赖倒转原则:
把父类都替换成它的子类,程序的行为没有变化。简单的说,子类型能够替换掉它们的父类型。

接口隔离原则:
每一个接口应该是一种角色,不多不少,不干不该干的事,该干的事都要干。

合成/聚合复用:
合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。它的设计原则是:要尽量使用合成/聚合,尽量不要使用继承。


工厂和单例模式

 

Fund-Service服务作为鲸技术对接外部商户的服务之一,使用标准基金文件对接外部商户。我们与商户是以【开放式基金业务数据交换协议】中规定的请求、确认文件进行交互的。

其中的交易确认文件即04文件有“认购确认(120)、申购确认(122)、赎回确认(124)、强行赎回确认(142)、认购结果(130)、定时定额申购确认(139)、ETF申购一次确认(191)、ETF申购二次确认(192)”等36个业务代码,如果我们采用if-else或switch-case方法实现,代码结构将会是像下面这样:

上面的代码如果分支少且分支固定,用if-else或switch-case是一个不错的选择。

然而,随着Fund-Service对接外部商户的增多,对接的产品类型有货基型、净值型等众多产品。交易确认文件04的业务类型势必会有36个业务代码中的多个。用if-else方式会影响代码的可维护性,添加一种新的业务代码就要添加一个分支,代码是罗列在一起的可读性也比较差。这里我们用了工厂模式,按业务类型拆分逻辑,实现类之间彼此相互独立。使得代码设计符合开闭原则:模块应对扩展开放,而对修改关闭。让我们先认识一下工厂模式吧。


工厂模式(Factory Pattern)

工厂模式是编程中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

目标定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

  1. 主要解决:解决接口选择的问题。

  2. 适用场景:我们明确地计划不同条件下创建不同实例时。

  3. 如何实现:让其子类实现工厂接口,返回的也是一个抽象的产品。

  4. 关键代码:创建过程在其子类执行。

Fund-Service中04交易确认文件相关类图:

这里的JrtFile04Maker处理器类是工厂类,AbstractJrtBusinessMaker是接口,JrtBusiness122Maker和JrtBusiness124Maker是两个实现类。AbstractJrtFileMaker是工厂模式的Client端,调用工厂类JrtFile04Maker的getRowDataList根据业务类型不同返回不同实例。JrtBusiness122Maker等业务类型的实例创建我们没有用new去实现,而是借助spring提供的单例模式自动注入。接下来让我们再认识一下单例模式。


单例模式(Singleton Pattern)

单例模式是最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  1. 主要解决:解决一个全局使用的实例频繁地创建与销毁。

  2. 适用场景:当你想控制实例数目,节省系统资源的时候。

  3. 如何实现:判断系统是否已经有这个单例,如果有则返回此单例,如果没有则创建。

  4. 关键代码:构造函数是私有的。

Fund-Service中单例的关键代码:

我们借助spring提供的自动注入机制实现了AbstractJrtBusinessMaker接口的业务处理器JrtBusiness122Maker、JrtBusiness124Maker等的实例注入到list中,并最终保存在缓存businessMakerMapCache中。这样工厂类JrtFile04Maker可以直接根据业务类型从businessMakerMapCache缓存中取得相应的处理器实现数据输出。


综上所述,Fund-Service服务在出04交易确认文件时客户端调用工厂类根据业务类型选择相应的处理器,而处理器的创建借助单例模式实现并注入到缓存中(试想这里不用单例模式而是每次都去new创建对象,商户的交易记录有成百上千……那将是一场灾难,尽管java的Garbage Collection垃圾收集机制很强大),处理器处理各自的业务逻辑并最终出数据文件。我们借助工厂模式优雅的拆分出业务代码分支,日后有新的业务代码接入只需添加实现类即可。最重要的是这符合模块应对扩展开放,而对修改关闭的原则!


工厂模式总结

优点
  1. 一个调用者想创建一个对象,只要知道其名称就可以了。 

  2. 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 

  3. 屏蔽产品的具体实现,调用者只关心产品的接口。


缺点

任务事物都有两面性,工厂模式也是如此。每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

注意事项

作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工  厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用  工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。


单例模式总结

优点
  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。

  2. 避免对资源的多重占用(比如写文件操作)。

缺点

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。


注意事项

getInstance() 方法中需要使用同步锁防止多线程同时进入造成instance 被多次实例化。




以上通过鲸技术的Fund-Service服务,向大家介绍了工厂和单例模式,希望可以通过这样一个真实的项目案例来加深大家对设计模式的理解,并在项目中更好的运用设计模式。           


-END-

文章转载自中航鲸技术,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论