查看原文
其他

从零开始学设计模式(一)——设计模式介绍及工厂模式

小暴说 云时代架构 2019-05-09


    选择“置顶公众号”,精品文章第一时间送达!


设计模式介绍

对于有经验的开发人员,学习设计模式有助于我们找到在软件开发过程中所面临的问题的最佳解决方案。一直以来软件都是为了用来解决现实生活中遇到的复杂问题而存在,设计模式(Design pattern)就像一套基础武功心法,每一式都代表了一类问题的最佳实践,且可根据实际情况组合使用。本系列文章笔者将带大家一起从零开始学习设计模式,后面会逐个剖析23种设计模式在Java中的具体实现,读者需具备基本的Java编程概念。


设计模式介绍


本次的系列教程内容和代码主要来源于 GitHub iluwater 设计模式在Java中的实现(Design patterns implemented in Java)
其官网地址:http://java-design-patterns.com/


说明


设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的


设计模式可以通过提供经过测试和验证的开发范例来加快开发过程


重用设计模式有助于防止可能导致重大问题的微妙问题,同时也提高了熟悉模式的程序员和架构师的代码可读性


什么是GOF(四人帮,全拼Gang of Four)


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


四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。

  • 对接口编程而不是对实现编程。

  • 优先使用对象组合而不是继承。


用途


设计模式的主要用途有两个,一是提供了一个标准的术语系统,且具体到特定的场景。如单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。二是提供了最佳的实践,设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计


设计模式的类型


根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。还有另一类设计模式:J2EE设计模式。这些设计模式从易到难可以分为三个等级:Difficulty-Beginner(难度-初学者), Difficulty-Intermediate(难度-中级) & Difficulty-Expert(难度-专家),后续文章我将从易到难的为大家介绍不同类型的设计模式。


序号模式&描述包括
1创建型模式
这些设计模式提供了一种在创建对象的同时
隐藏创建逻辑的方式,而不是使用new运算符
直接实例化对象。这使得程序在判断针对
某个给定实例需要创哪些对象时更加灵活
工厂模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
2结构型模式
这些设计模式关注类和对象的组合。
继承的概念被用来组合接口
和定义组合对象获得新功能的方式。
适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
过滤器模式(Filter、Criteria Pattern)
组合模式(Composite Pattern)
装饰器模式(Decorator Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
3行为型模式
这些设计模式特别关注对象之间的通信。
责任链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
备忘录模式(Memento Pattern)
观察者模式(Observer Pattern)
状态模式(State Pattern)
空对象模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
访问者模式(Visitor Pattern)
4J2EE模式
这些设计模式特别关注表示层。
这些模式是由 Sun Java Center 鉴定的。
MVC 模式(MVC Pattern)
业务代表模式(Business Delegate Pattern)
组合实体模式(Composite Entity Pattern)
数据访问对象模式(Data Access Object Pattern)
前端控制器模式(Front Controller Pattern)
拦截过滤器模式(Intercepting Filter Pattern)
服务定位器模式(Service Locator Pattern)
传输对象模式(Transfer Object Pattern)

下面一张图片整体描述了一下设计模式之间的关系:


来源于runoob网站


设计模式的六大原则


1、开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。


2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。


3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。


4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。


5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。


6、合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。


设计模式的思考


在学习和理解设计模式之前,你应该熟悉一些编程/软件设计原则。 所有的设计都应该尽可能简单,从最简单的事情开始,慢慢延伸,这可能是工作学习的原理。


只有在实际要求可扩展性,需要复杂性和套路模式时,才应该引入它们(设计模式)。 一旦你熟悉了这些设计模式的概念,你就可以根据实际工程情况选择最适合的模式来优雅的实现工程目标。


工厂模式

工厂模式也被称之为虚拟构造函数(Virtual Constructor),是Java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。


在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象


意图


定义用于创建对象的接口,但是让子类决定实例化哪个类。factory方法允许类将实例化推迟到子类


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

何时使用:我们明确地计划不同条件下创建不同实例时。

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

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


解释


现实世界的例子

铁匠制造武器。精灵需要精灵武器,兽人需要兽人武器。根据手头的顾客,召集合适类型的铁匠


简而言之

它提供了一种将实例化逻辑委托给子类的方法


维基百科说

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.
(在基于类的编程中,factory方法模式是一种创建模式,它使用factory方法来处理创建对象的问题,而不必指定将要创建的对象的确切类。这是通过调用factory方法(在接口中指定并由子类实现,或者在基类中实现并可选地由派生类重写)来实现的,而不是通过调用构造函数来实现的。)


程序代码示例


以现实世界的例子铁匠制造武器为例子,需要什么样的武器类型我们就召唤对应类型的铁匠。程序类图如下:


铁匠接口实现类图.png


首先我们有一个铁匠接口(定义了一个制造武器的方法)和一些精灵铁匠、兽人铁匠实现类:

public interface Blacksmith {
 Weapon manufactureWeapon(WeaponType weaponType);
}

public class ElfBlacksmith implements Blacksmith {
 public Weapon manufactureWeapon(WeaponType weaponType) {
   return new ElfWeapon(weaponType);
 }
}

public class OrcBlacksmith implements Blacksmith {
 public Weapon manufactureWeapon(WeaponType weaponType) {
   return new OrcWeapon(weaponType);
 }
}


其次我们有一个武器接口(定义了一个获取武器类型的方法)和一些精灵武器、兽人武器实现类:

/**
* Weapon interface.
*/

public interface Weapon {

 WeaponType getWeaponType();

}
/**
* ElfWeapon.
*/

public class ElfWeapon implements Weapon {

 private WeaponType weaponType;

 public ElfWeapon(WeaponType weaponType) {
   this.weaponType = weaponType;
 }

 @Override
 public String toString() {
   return "Elven " + weaponType;
 }

 @Override
 public WeaponType getWeaponType() {
   return weaponType;
 }
}

/**
* OrcWeapon.
*/

public class OrcWeapon implements Weapon {

 private WeaponType weaponType;

 public OrcWeapon(WeaponType weaponType) {
   this.weaponType = weaponType;
 }

 @Override
 public String toString() {
   return "Orcish " + weaponType;
 }

 @Override
 public WeaponType getWeaponType() {
   return weaponType;
 }
}


最后,随着顾客的到来,正确类型的铁匠被召唤出来,要求制造武器

public class App {

 private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

 private final Blacksmith blacksmith;
 
 /**
  * Creates an instance of <code>App</code> which will use <code>blacksmith</code> to manufacture
  * the weapons for war.
  * <code>App</code> is unaware which concrete implementation of {@link Blacksmith} it is using.
  * The decision of which blacksmith implementation to use may depend on configuration, or
  * the type of rival in war.
  * @param blacksmith a non-null implementation of blacksmith
  */

 public App(Blacksmith blacksmith) {
   this.blacksmith = blacksmith;
 }
 
 /**
  * Program entry point
  *
  * @param args command line args
  */

 public static void main(String[] args) {
   // Lets go to war with Orc weapons
   App app = new App(new OrcBlacksmith());
   app.manufactureWeapons();
   
   // Lets go to war with Elf weapons
   app = new App(new ElfBlacksmith());
   app.manufactureWeapons();
 }
 
 private void manufactureWeapons() {
   Weapon weapon;
   weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR);
   LOGGER.info(weapon.toString());
   weapon = blacksmith.manufactureWeapon(WeaponType.AXE);
   LOGGER.info(weapon.toString());
 }
}


运行App程序结果:

app类运行结果输出.png


适用场景


当遇到如下三种情况时,应使用工厂模式:

  1. 一个类不能预测它必须创建的对象的类

  2. 一个类希望它的子类指定它创建的对象

  3. 类将责任委托给几个助手子类中的一个,并且你希望本地化哪个助手子类是委托的责任


Java中的现实例子

  • java.util.Calendar

  • java.util.ResourceBundle

  • java.text.NumberFormat

  • java.nio.charset.Charset

  • java.net.URLStreamHandlerFactory

  • java.util.EnumSet

  • javax.xml.bind.JAXBContext


优缺点


优点:

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

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

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


缺点:
每次增加一个产品时,都需要增加一个具体实现类和修改对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事


最后


工厂模式作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂模式,比如设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个通讯接口。


对于简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式,因为使用工厂模式必然要引入一个工厂类,这会增加系统的复杂度,切不可为了设计模式而模式。


之后我们将在工厂模式的基础上继续延伸介绍抽象工厂模式,难度系统为中级


来自:小暴说

链接:https://www.jianshu.com/p/e6f678739f42;https://www.jianshu.com/p/eee619661691

(本文版权归原作者所有。转载文章仅为传播更多信息之目的,如有侵权请与我们联系,我们将及时处理。)


- End -

推荐阅读

剖析网联业务架构

API管理的正确姿势--API Gateway

深入浅出分布式缓存的通用方法

架构设计之「数据库集群方案」

看完秒懂对称加密、非对称加密、数字签名

亿万级数据处理的高效解决方案

支付系统

支付公司账务系统的那些事

阿里面试官的分享Java面试中需要准备哪些多线程并发的技术要点

免费的教学视频:微服务实施的最佳实践、一致性、压测、容量评估、调用链一网打尽


【推荐书籍】




      

        扫码购买


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存