0%

设计模式(一)设计模式概述

一、设计模式的概念

设计模式解决的是一类问题。
工厂模式就是为了解决类创建的问题,
适配器模式则是为了解决类接口不匹配的问题。

(1)学习这些模式是一个方面,另一方面更要了解模式中的思想。
(2)设计模式虽然可以使设计变得更精妙,但滥用设计模式会适得其反。

二、设计模式的组成

在描述一个设计模式时,至少需要包含四个方面:
模式名称(Pattern name)、
问题(Problem)、
解决方案(Solution)、
效果(Consequence)。

三、GoF设计模式

(补充)简单工厂

1、写一个工厂类,使用静态方法,根据传参来new不同的产品对象
2、客户端调用这个工厂类的静态方法,就可以获取想要的产品

(1)Factory Method 模式。工厂模式

Factory Method 模式提供了一种延迟创建类的方法,使用 这个方法可以在运行期由子类决定创建哪一个类的实例。
1、写一个抽象工厂类,每个工厂实现类负责将new不同种类的产品。
2、客户端new不同的工厂实现类就可以获得不同的产品。

(2)Abstract Factory 模式。抽象工厂模式

Abstract Factory 又称为抽象工厂模式,该模式主要为解决 复杂系统中对象创建的问题。抽象工厂模式提供了一个一致的对象创建接口来创建一系列具 有相似基类或相似接口的对象。
1、写一个抽象工厂类,然后每个工厂实现类负责new一组不同的种类的产品。
2、客户端new需要某一组的产品,只需要new一个工厂即可。

(3)Builder 模式。建造者模式

Builder 模式与 Abstract Factory 模式非常类似,但 Builder 模式是逐步地构造出一个复杂对象,并在最后返回对象的实例。Builder 模式可以把复杂对象的创建与表示分离,使得同样的创建过程可以创建不同的表示。
1、Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
2、ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。在建造过程完成后,提供产品的实例。
3、Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建,是一个具体的类。
4、Product:要创建的复杂对象类。

1
2
3
4
5
6
7
8
9
public class Director {

public Product constructProduct(ConcreteBuilder concreteBuilder){
concreteBuilder.buildBasic();
concreteBuilder.buildWalls();
concreteBuilder.roofed();
return concreteBuilder.buildProduct();
}
}

(4)Prototype 模式。原型模式

Prototype 模式可以根据原型实例制定创建的对象的种类,并通过深复制这个原型来创建新的对象。Prototype 模式有着同 Abstract Factory 模式和 Builder 模式相同的效果,不过当需要实例化的类是在运行期才被指定的而且要避免创建一个与产品 曾是平行的工厂类层次时,可以使用 Prototype 模式。使用 Prototype 模式可以在运行时 增加或减少原型,比 Abstract Factory 和 Builder 模式更加灵活。

1
2
Prototype pro = new Prototype();
Prototype pro1 = (Prototype)pro.clone();

(5)Singleton 模式。单例模式

Singleton 模式也是一种很有代表性的模式。使用 Singleton 模式 可以保证一个类仅有一个实例,从而可以提供一个单一的全局访问点。

饿汉式

在定义类的静态私有变量同时进行实例化。

1
2
3
4
5
6
7
public class Singleton {  
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

好处:线程安全;获取实例速度快
缺点:类加载即初始化实例,内存浪费

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

优点:线程安全,进行双重检查,保证只在实例未初始化前进行同步,效率高
缺点:实例非空判断,耗费一定资源

静态内部类

1
2
3
4
5
6
7
8
9
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

这种方式利用了classloder的机制来保证初始化instance时只有一个线程,但singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。如果实例化instance很消耗资源,我们想让他延迟加载,此外,我们不希望在Singleton类加载时就实例化,因为不能确保Singleton类还可能在其他的地方被主动使用从而被加载。这个时候,这种方式相比饿汉式就显得更合理。

枚举

1
2
3
4
5
public enum Singleton {  
INSTANCE;
public void whateverMethod() {
}
}

优点:写法简单,天然线程安全,可防止反射生成实例,是Effective Java作者Josh Bloch提倡的方式。

(6)Adapter 模式。适配者模式

Adapter 模式可以解决系统间接口不相容的问题。通过 Adapter 可以把类的接口转化为客户程序所希望的接口,从而提高复用性。
实现原来的接口,方法体却是另一个对象的实现。

接口适配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Mp4{
void playMp4();
}
interface Avi{
void playAvi();
}
class VideoPlayer implements Mp4{
@Override
public void playMp4() {
System.out.println("播放Mp4格式的视频文件.");
}
}
class FormatFactory extends VideoPlayer implements Avi{
@Override
public void playAvi() {
//转换成MP4格式的视频
playMp4();
}
}
public static void main(String[] args) {
Mp4 mp4=new VideoPlayer();
mp4.playMp4();
Avi avi=new FormatFactory();
avi.playAvi();
}

类适配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class FormatFactory2 implements Rvmb{
private Mp4 mp4;
public FormatFactory2(Mp4 mp4) {
this.mp4=mp4;
}
@Override
public void playRvmb() {
mp4.playMp4();
}
}
public static void main(String[] args) {
Rvmb rvmb=new FormatFactory2(new VideoPlayer());
rvmb.playRvmb();
}

(7)Bridge 模式。桥接模式

Bridge 模式把类的抽象部分同实现部分相分离,这样类的抽象和实现都可以独立地变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Pen{
void write();
}
class RedPen implements Pen{
@Override
public void write() {}
}
abstract class Paper{
protected Pen pen;
void setPen(Pen pen){
this.pen=pen;
}
abstract void writing();
}
class ExaminationPaper extends Paper{
@Override
void writing() {}
}
public static void main(String[] args) {
Paper paper=new ExaminationPaper();
paper.setPen(new RedPen());
paper.writing();
}

(8)Composite 模式。复合模式

Composite 模式提供了一种以树形结构组合对象的方法,使用Composite 可以使单个对象和组合后的对象具有一致性以提高软件的复用性。

(9)Decorator 模式。装饰模式

Decorator 模式可以动态地为对象的某一个方法增加更多的功能。在很多时候,使用 Decorator 模式可以不必继承出新的子类从而维护简洁的类继承结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Beverage{} // 饮料接口
public class CoffeeBean implements Beverage{} // 具体被装饰的对象类
public class Decorator implements Beverage{} // 装饰类
public class Milk extends Decorator{ // 具体装饰类,给咖啡加入牛奶
private String description = "加了牛奶!";
private Beverage beverage = null;
public Milk(Beverage beverage){
this.beverage = beverage;
}
public String getDescription(){
return beverage.getDescription()+"\n"+description;
}
}
public static void main(String[] args) {
Beverage beverage = new CoffeeBean();
beverage = new Milk(beverage); // 给咖啡加了牛奶
System.out.println(beverage.getDescription());
}

(10)Facade 模式。外观模式,门面模式

Facade 模式为一组类提供了一致的访问接口。使用 Facade 可以 封装内部具有不同接口的类,使其对外提供统一的访问方式。Facade 模式在 J2EE 系统开 发中发展为 Session Facade 模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FacadePattern {
public static void main(String[] args) {
Facade f = new Facade();
f.method();
}
}
class Facade { //外观角色
private SubSystem01 obj1 = new SubSystem01();
private SubSystem02 obj2 = new SubSystem02();
private SubSystem03 obj3 = new SubSystem03();
public void method() {
obj1.method1();
obj2.method2();
obj3.method3();
}
}

(11)Flyweight 模式。享元模式,共享元素

Flyweight 模式可以共享大量的细粒度对象,从而节省创建对象 所需要分配的空间,不过在时间上的开销会变大。

1
2
3
4
5
6
7
8
public Flyweight factory(int state){
if(map.containsKey(state)){
return (Flyweight)map.get(state);
}else{
map.put(state, new ConcreteFlyweight(state));
return (Flyweight)map.get(state);
}
}

(12)Proxy 模式。代理模式

Proxy 模式为对象提供了一种访问代理对象,通过对象 Proxy 可以控制客户程序的访问。例如:访问权限的控制、访问地址的控制、访问方式的控制等, 甚至可以通过 Proxy 将开销较大的访问化整为零,提高访问效率。

1
2
3
4
Customer customer=new Customer();
customer.setCash(120000);
BuyCarProxy buyCarProxy=new BuyCarProxy(customer);
buyCarProxy.buyCar();

(13)Interpreter 模式。解释器模式

定义了一个解释器,来解释遵循给定语言和文法的句子。
只需要向计算机输入一个句子或文件,就能按照预定的文法规则来对句子或文件进行解释。
例如输入“1+2+3-4+1”时,将输出计算结果为3。

(14)Template Method 模式。模板方法模式

定义一个操作的模板,其中的一些步骤会在子类中实现,以适应不同的情况。
在父类中定义处理流程的框架,在子类中实现具体的处理方式。

(15)Chain of Responsibility 模式。责任链模式

Chain of Responsibility 模式把可以响应请求的对象 组织成一条链,并在这条对象链上传递请求,从而保证多个对象都有机会处理请求而且可以 避免请求方和相应方的耦合。

一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一个是承担责任,二是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。

在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。纯的责任链模式的例子是不容易找到的,一般看到的例子均是不纯的责任链模式的实现。

(16)Command 模式。命令模式

将请求封装为对象,从而增强请求的能力,如参数化、排队、 记录日志等。

Command有时也被称为事件(event)。它与“事件驱动编程”中的“事件”是一样的意思。当发生点击鼠标、按下键盘按键等事件时,我们可以先将这些事件作成实例,然后按照发生顺序放入队列中。接着,再依次去处理它们。在GUI(graphical user interface)编程中,经常需要与“事件”打交道。即为在有多个命令,并且这些命令有一定的逻辑顺序,且可能需要保存的这些命令的数据,那么可以使用Command设计模式。

(17)Iterator 模式。迭代器模式

Iterator 模式提供了顺序访问一个对象集合中的各元素的方法, 使用 Iterator 可以避免暴露集合中对象的耦合关系。

(18)Mediator 模式。仲裁者模式、中介者模式

Mediator 模式可以减少系统中对象间的耦合性。Mediator 模式 使用中介对象封装其他的对象,从而使这些被封装的对象间的关系就成了松散耦合。

例如:QQ聊天

(19)Memento 模式。备忘录模式

Memento 模式提供了一种捕获对象状态的方法,且不会破坏对 象的封装。并且可以在对象外部保存对象的状态,并在需要的时候恢复对象状态。

1
2
3
4
5
6
7
8
public static void main(String[] args) {
List<Memento> savedTimes = new ArrayList<>();
Life life = new Life();
life.set("3000 A.D.");
savedTimes.add(life.saveToMemento());
life.set("4000 A.D.");
life.restoreFromMemento(savedTimes.get(0));
}

(20)Observer 模式。观察者模式

Observer 模式提供了将对象的状态广播到一组观察者的方式, 从而可以让每个观察者随时可以得到对象更新的通知。

(21)State 模式。状态模式

State 模式允许一个对象在其内部状态改变的时候改变它的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void setState(State state) {
System.out.println("订单信息已更新!");
this.state = state;
this.state.handle();
}
public static void main(String [] args) {
Context context = new Context();
context.setState(new Booked());
context.setState(new Payed());
context.setState(new Sended());
context.setState(new InWay());
context.setState(new Recieved());
}

(22)Strategy 模式。策略模式

使用 Strategy 模式可以让对象中算法的变化独立于客户。

1
2
3
4
5
状态模式和策略模式的不同点:
策略模式中,类的功能是根据当前条件主动更改;
状态模式中,类的功能是被动由当前状态更改;
策略模式中每个行为或算法之间没有关联;
状态模式中的状态之间有关联,并且状态本身控制着状态转移;

(23)Visitor 模式。访问者模式

表示对某对象结构中各元素的操作,使用 Visitor 模式可以在不改
变各元素类的前提下定义作用于这些元素的新操作。

双重分发

四、其他设计模式

(1)Intercepting Filter 模式。拦截过滤器模式

在 J2EE 的 BPS(Basic Programming System,基本编程系 统)应用框架下,在真正响应客户端请求前经常需要进行一些预处理,如客户身份验证、客 户 Session 的合法性验证、字符集转码、客户请求记录等。

(2)Session Facade 模式。

在 J2EE 开发领域,人们把Session Bean 和 Facade 模式结合起来, 封装业务逻辑的接口,形成了 Session Facade 模式。

五、设计模式与软件架构

软件架构更倾向于从整体和全局上描述软件的组成。
设计模式更侧重于类与类、对象与对象之间的关系。
设计模式和软件架构是面向不同层次问题的解决方案。

六、设计模式分类

设计模式分为三类,分别为创建型、结构型和行为型。