阿里P7大牛细说架构——设计模式专栏

557 阅读11分钟

设计模式介绍

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

文末有福利放送,感谢读者的阅读。

设计模式(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(难度-专家),后续文章我将从易到难的为大家介绍不同类型的设计模式。

阿里P7大牛细说架构——设计模式专栏

阿里P7大牛细说架构——设计模式专栏

阿里P7大牛细说架构——设计模式专栏

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

阿里P7大牛细说架构——设计模式专栏

设计模式的六大原则

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方法(在接口中指定并由子类实现,或者在基类中实现并可选地由派生类重写)来实现的,而不是通过调用构造函数来实现的。)


程序代码示例

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

阿里P7大牛细说架构——设计模式专栏

铁匠接口实现类图.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程序结果:

阿里P7大牛细说架构——设计模式专栏

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就可以完成创建的对象,无需使用工厂模式,因为使用工厂模式必然要引入一个工厂类,这会增加系统的复杂度,切不可为了设计模式而模式。

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

下面是笔者给各位准备的福利:

阿里P7大牛细说架构——设计模式专栏

阿里P7大牛细说架构——设计模式专栏


高可用架构篇

第01节--ZooKeeper集群的安装、配置、高可用测试

第02节--ZooKeeper集群的升级、迁移

第03节--ActiveMQ高可用集群(ZooKeeper+LevelDB)安装、配置、高可用测试

第04节--ActiveMQ高可用+负载均衡集群的安装、配置、高可用测试

第05节--Redis集群的安装(Redis3+CentOS)

第06节--Redis集群的高可用测试(含Jedis客户端的使用)

第07节--Redis集群的扩展测试

第08节--Keepalived+Nginx实现高可用Web负载均衡

第09节--FastDFS集群的安装

第10节--FastDFS集群的配置

第11节--FastDFS集群的使用(结合简易版支付系统)

第12节--使用Redis3.0集群实现Tomcat集群的Session共享

第13节--MySQL源码编译安装(CentOS-6.6+MySQL-5.6)

第14节--MySQL主从复制的配置(CentOS-6.6+MySQL-5.6)

第15节--MyCat在MySQL主从复制基础上实现读写分离

第16节--MyCat集群部署(HAProxy+MyCat)

第17节--MyCat高可用负载均衡集群实现(HAProxy+Keepalived+MyCat)

关注我:加入主页分享的程序员交流,获取往期Java高级架构资料、视频

Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、

高并发等架构技术