设计模式的六大原则

设计模式对于程序员而言并不陌生,每个程序员在编程时都会或多或少地接触到设计模式。无论是在大型程序的架构中,亦或是在源码的学习中,设计模式都扮演着非常重要的角色。面向对象结合设计模式,才能真正体会到程序变得可维护、可复用、可扩展、灵活性好。
谈到设计模式就不得不说设计模式之魂:六大原则。
设计模式基于六大原则
  • 开闭原则:一个软件实体(如类、模块和函数)应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
  • 单一职责原则:一个类只做一件事,一个类应该只有一个引起它修改的原因。
  • 里氏替换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
  • 依赖倒置原则:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。关键点:1. 高层模块不应该依赖低层模块,两者都应该依赖其抽象;2. 抽象不应该依赖细节;3. 细节应该依赖抽象
  • 迪米特法则:又名「最少知道原则」,一个软件实体应当尽可能少地与其他实体发生相互作用。
  • 接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
所有的设计模式都是为了程序能更好的满足这六大原则。设计模式一共有 23 种,5种构建型模式:
  • 工厂方法模式
  • 抽象工厂模式
  • 单例模式
  • 建造者模式
  • 原型模式
构建型模式,主要解决如何灵活创建对象或者类的问题。
7 种结构型模式:
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰模式
  • 外观模式
  • 享元模式
  • 代理模式
结构型模式,主要用于将类或对象进行组合从而构建灵活而高效的结构。
11种行为型设计模式:
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 策略模式
  • 状态模式
  • 模板方法模式
  • 访问者模式
行为型设计模式,主要解决类或者对象直接互相通信的问题。
下面介绍工作中常常使用的建造者模式,Builder 模式。

定义

将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

使用场景

如果你学了个东西,都不知道用来解决什么问题,你说有什么用?理解使用场景的的重要性要远高于你是不是会实现这个模式,因为只要你知道什么问题可以使用builder模式来解决,那你即使不会写,也可以在调查相关资料后完成。这里不讲一些大而正确的术语来把你搞蒙,我们只针对具体的问题,至于延展性的思考,随着你知识的增长,逐渐会明白的。
当一个类的构造函数参数个数超过 4 个,而且这些参数有些是可选的参数,考虑使用构造者模式。

解决的问题

当一个类的构造函数参数超过 4 个,而且这些参数有些是可选的时,我们通常有两种办法来构建它的对象。例如现在有如下一个类计算机类Computer,其中cpu与ram是必填参数,而其它 3 个是可选参数,那么如何构造这个类的实例呢  
通常有两种常用的方式
publicclass Computer {privateString cpu;//必须privateString ram;//必须private int usbCount;//可选privateString keyboard;//可选privateString display;//可选}
第一:折叠构造函数模式,这个我们经常用,如下代码所示
publicclass Computer { ...public Computer(String cpu, String ram) {this(cpu, ram, 0); }public Computer(String cpu, String ram, int usbCount) {this(cpu, ram, usbCount, "罗技键盘"); }public Computer(String cpu, String ram, int usbCount, String keyboard) {this(cpu, ram, usbCount, keyboard, "三星显示器"); }public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {this.cpu = cpu;this.ram = ram;this.usbCount = usbCount;this.keyboard = keyboard;this.display = display; }}
第二种:Javabean 模式,如下所示
publicclassComputer { ...public String getCpu() {return cpu; }publicvoidsetCpu(String cpu) {this.cpu = cpu; }public String getRam() {return ram; }publicvoidsetRam(String ram) {this.ram = ram; }publicintgetUsbCount() {return usbCount; }...}
那么这两种方式有什么弊端呢?
第一种主要是使用及阅读不方便。你可以想象一下,当要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了。。。那酸爽谁用谁知道。
第二种方式在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错。
为了解决这两个痛点,builder模式就横空出世了。

如何实现

  1. 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
  2. 在Computer中创建一个private的构造函数,参数为Builder类型
  3. 在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram。
  4. 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
  5. 在Builder中创建一个build()方法,在其中构建Computer的实例并返回
下面代码就是最终的样子
publicclassComputer{privatefinal String cpu;//必须privatefinal String ram;//必须privatefinal int usbCount;//可选privatefinal String keyboard;//可选privatefinal String display;//可选private Computer(Builder builder){this.cpu=builder.cpu;this.ram=builder.ram;this.usbCount=builder.usbCount;this.keyboard=builder.keyboard;this.display=builder.display; }public static classBuilder{private String cpu;//必须private String ram;//必须private int usbCount;//可选private String keyboard;//可选private String display;//可选public Builder(String cup,String ram){this.cpu=cup;this.ram=ram; }public Builder setUsbCount(int usbCount) {this.usbCount = usbCount;returnthis; }public Builder setKeyboard(String keyboard) {this.keyboard = keyboard;returnthis; }public Builder setDisplay(String display) {this.display = display;returnthis; } public Computer build(){return new Computer(this); } }//省略getter方法}

如何使用

在客户端使用链式调用,一步一步的把对象构建出来。
Computer computer=new Computer.Builder("因特尔","三星") .setDisplay("三星24寸") .setKeyboard("罗技") .setUsbCount(2) .build();
构建者模式是一个非常实用而常见的创建类型的模式,例如Flink、Flume 使用了此模式。

bigdata_ny
继续阅读
阅读原文