各个设计模式解析

创建型模式

单例模式 Singleton

饿汉单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class EagerSingleton {
//饿汉单例模式
//在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
private static EagerSingleton instance = new EagerSingleton();//静态私有成员,已初始化

private EagerSingleton(){
//私有构造函数
}

public static EagerSingleton getInstance(){
//静态,不用同步(类加载时已初始化,不会有多线程的问题)
return instance;
}


}

在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。

懒汉单例

懒汉式,线程不安全

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

懒汉式,线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {

private volatile static Singleton instance; //声明成 volatile

private Singleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

}

volatile增加的作用主要是为了防止指令重排

1
if (instance == null)

这段代码被其他线程抢占执行,synchronize里面正好有指针,但未开辟内存空间,就会出现问题

优点

提供了对唯一实例的受控访问。
因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

缺点

由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
单例类的职责过重,在一定程度上违背了”单一职责原则”。
因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术
因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

建造者模式 Builder

建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Data {
private String data;

private Data(Builder builder) {
data = builder.data;
}

public static final class Builder {
private String data;

public Builder() {
}

public Builder data(String val) {
data = val;
return this;
}

public Data build() {
return new Data(this);
}
}
}

优点

在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合”开闭原则”。

缺点

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

工厂模式 产品Factory

简单工厂(静态工厂)

简单工厂模式又称静态工厂方法模式。
存在的目的很简单:定义一个用于创建对象的接口。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//###### 产品类
abstract class BMW {
public BMW(){

}
}
public class BMW320 extends BMW {
public BMW320() {
System.out.println("制造-->BMW320");
}
}
public class BMW523 extends BMW{
public BMW523(){
System.out.println("制造-->BMW523");
}
}



//###### 工厂类
public class Factory {
public static BMW createBMW(int type) {
switch (type) {
case 320:
return new BMW320();
case 523:
return new BMW523();
default:
break;
}
return null;
}
}



//###### 客户类
public class Customer {
public static void main(String[] args) {
BMW bmw320 = Factory.createBMW(320);
BMW bmw523 = Factory.createBMW(523);
}
}

优点

使用工厂类,不必管这些对象究竟如何创建及如何组织的,明确了各自的职责和权利。

缺点

违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;如果需要添加新的类,则就需要改变工厂类了。对系统的维护和扩展非常不利;

适用范围

简单工厂模式:工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。

工厂方法 Factory产品类型

由之前的简单工厂模式(静态工厂模式)改造

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
26
27
interface FactoryBMW {  
BMW createBMW();
}
//###### 工厂类
public class FactoryBMW320 implements FactoryBMW{
@Override
public BMW320 createBMW() {
return new BMW320();
}
}
//###### 工厂类
public class FactoryBMW523 implements FactoryBMW {
@Override
public BMW523 createBMW() {
return new BMW523();
}
}
//###### 客户类
public class Customer {
public static void main(String[] args) {
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
BMW320 bmw320 = factoryBMW320.createBMW();

FactoryBMW523 factoryBMW523 = new FactoryBMW523();
BMW523 bmw523 = factoryBMW523.createBMW();
}
}

优点

工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。
简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码)而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。工厂方法模式完全满足OCP,即它有非常良好的扩展性。

缺点

不易于维护,假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦

  • 适用范围:当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时,当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法。

抽象工厂 AbstractFactory

抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。

如果以上实例牵扯很多附属的实例,例如车牵扯引擎与空调

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//发动机以及型号    
public interface Engine {

}
public class EngineA extends Engine{
public EngineA(){
System.out.println("制造-->EngineA");
}
}
public class EngineBextends Engine{
public EngineB(){
System.out.println("制造-->EngineB");
}
}

//空调以及型号
public interface Aircondition {

}
public class AirconditionA extends Aircondition{
public AirconditionA(){
System.out.println("制造-->AirconditionA");
}
}
public class AirconditionB extends Aircondition{
public AirconditionB(){
System.out.println("制造-->AirconditionB");
}
}




//创建工厂的接口
public interface AbstractFactory {
//制造发动机
public Engine createEngine();
//制造空调
public Aircondition createAircondition();
}


//为宝马320系列生产配件
public class FactoryBMW320 implements AbstractFactory{

@Override
public Engine createEngine() {
return new EngineA();
}
@Override
public Aircondition createAircondition() {
return new AirconditionA();
}
}
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {

@Override
public Engine createEngine() {
return new EngineB();
}
@Override
public Aircondition createAircondition() {
return new AirconditionB();
}
}



public class Customer {
public static void main(String[] args){
//生产宝马320系列配件
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
factoryBMW320.createEngine();
factoryBMW320.createAircondition();

//生产宝马523系列配件
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
factoryBMW320.createEngine();
factoryBMW320.createAircondition();
}
}

优点

抽象工厂模式主要在于应对”新系列”的需求变化。

分离了具体的类,抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。
这使得改变一个应用的具体工厂变得很容易。
它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。它有利于产品的一致性。当一个系列的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要,而抽象工厂很容易实现这一点。抽象工厂模式有助于这样的团队的分工,降低了模块间的耦合性,提高了团队开发效率。

缺点

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。

抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类的实例。每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结果。

适用范围

一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。这个系统有多于一个的产品族,而系统只消费其中某一产品族。同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

原型模式 Prototype

  • 实现Cloneable接口。只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
  • 重写Object类中的clone方法。Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
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
26
class Prototype implements Cloneable {    
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
prototype.list = (ArrayList) this.list.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
class ConcretePrototype extends Prototype{
public void show(){
System.out.println("原型模式实现类");
}
}
public class Client {
public static void main(String[] args){
ConcretePrototype cp = new ConcretePrototype();
for(int i=0; i< 10; i++){
ConcretePrototype clonecp = (ConcretePrototype)cp.clone();
clonecp.show();
}
}
}

深浅拷贝

Object类的clone方法只会拷贝对象中的基本的数据类型(8种基本数据类型byte,char,short,int,long,float,double,boolean)

对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

结构型模式

适配器模式 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类  
class Adaptee {
public void specificRequest() {
System.out.println("被适配类具有 特殊功能...");
}
}

// 目标接口,或称为标准接口
interface Target {
public void request();
}

// 具体目标类,只提供普通功能
class ConcreteTarget implements Target {
public void request() {
System.out.println("普通类 具有 普通功能...");
}
}

// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{
public void request() {
super.specificRequest();
}
}

// 测试类
public class Client {
public static void main(String[] args) {
// 使用普通功能类
Target concreteTarget = new ConcreteTarget();
concreteTarget.request();

// 使用特殊功能类,即适配类
Target adapter = new Adapter();
adapter.request();
}
}


//普通类 具有 普通功能...
//被适配类具有 特殊功能...

适用范围

SpringMVC RequestMappingHandlerAdapter

invocableMethod.invokeAndHandle运用了这种模式
与HandlerAdapter#handle结合使用了起来

装饰者模式 Decorator

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
public class DesignPatterns {
public static void main(String[] args) {
//测试类,看一下你就会发现,跟java的I/O操作有多么相似
Human person = new Person();
Decorator decorator = new Decorator_two(new Decorator_first(
new Decorator_zero(person)));
decorator.wearClothes();
decorator.walkToWhere();
}
}


//定义被装饰者
interface Human {
public void wearClothes();

public void walkToWhere();
}

//定义装饰者
abstract class Decorator implements Human {
private Human human;

public Decorator(Human human) {
this.human = human;
}

public void wearClothes() {
human.wearClothes();
}

public void walkToWhere() {
human.walkToWhere();
}
}

//下面定义三种装饰,这是第一个,第二个第三个功能依次细化,即装饰者的功能越来越多
class Decorator_zero extends Decorator {

public Decorator_zero(Human human) {
super(human);
}

public void goHome() {
System.out.println("进房子。。");
}

public void findMap() {
System.out.println("书房找找Map。。");
}

@Override
public void wearClothes() {
// TODO Auto-generated method stub
super.wearClothes();
goHome();
}

@Override
public void walkToWhere() {
// TODO Auto-generated method stub
super.walkToWhere();
findMap();
}
}

class Decorator_first extends Decorator {

public Decorator_first(Human human) {
super(human);
}

public void goClothespress() {
System.out.println("去衣柜找找看。。");
}

public void findPlaceOnMap() {
System.out.println("在Map上找找。。");
}

@Override
public void wearClothes() {
// TODO Auto-generated method stub
super.wearClothes();
goClothespress();
}

@Override
public void walkToWhere() {
// TODO Auto-generated method stub
super.walkToWhere();
findPlaceOnMap();
}
}

class Decorator_two extends Decorator {

public Decorator_two(Human human) {
super(human);
}

public void findClothes() {
System.out.println("找到一件D&G。。");
}

public void findTheTarget() {
System.out.println("在Map上找到神秘花园和城堡。。");
}

@Override
public void wearClothes() {
// TODO Auto-generated method stub
super.wearClothes();
findClothes();
}

@Override
public void walkToWhere() {
// TODO Auto-generated method stub
super.walkToWhere();
findTheTarget();
}
}

//定义被装饰者,被装饰者初始状态有些自己的装饰
class Person implements Human {

@Override
public void wearClothes() {
// TODO Auto-generated method stub
System.out.println("穿什么呢。。");
}

@Override
public void walkToWhere() {
// TODO Auto-generated method stub
System.out.println("去哪里呢。。");
}
}


/*
穿什么呢。。
进房子。。
去衣柜找找看。。
找到一件D&G。。
去哪里呢。。
书房找找Map。。
在Map上找找。。
在Map上找到神秘花园和城堡。。
*/

适用范围

动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活。

设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性
同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。

现在需要一个汉堡,主体是鸡腿堡,可以选择添加生菜、酱、辣椒等等许多其他的配料,这种情况下就可以使用装饰者模式。

只要嵌套装饰者,装饰者的行为也会添加上去

优点

装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合”开闭原则”

缺点

使用装饰模式进行系统设计时将产生很多小对象
这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。
这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

代理模式 Proxy

给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用

通过引入代理对象的方式来间接访问目标对象

代购(代理对象) 代替 我(真实对象) 去买Mac(间接访问的操作)

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public interface Subject {  
public void buyMac();
}


//真实对象
public class RealSubject implement Subject{
@Override
public void buyMac() {
System.out.println(”买一台Mac“);
}
}

//代理对象类(Proxy)
public class Proxy implements Subject{

@Override
public void buyMac{

//引用并创建真实对象实例,即"我"
RealSubject realSubject = new RealSubject();

//调用真实对象的方法,进行代理购买Mac
realSubject.buyMac();
//代理对象额外做的操作
this.WrapMac();
}

public void WrapMac(){
System.out.println("用盒子包装好Mac");
}
}

public class ProxyPattern {
public static void main(String[] args){
Subject proxy = new Proxy();
proxy.buyMac();
}
}

//买一台Mac
//用盒子包装好Mac

优点

防止直接访问目标对象给系统带来的不必要复杂性。
协调调用者和被调用者,降低了系统的耦合度
代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用

缺点

由于在客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢;
实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。

门面模式(外观模式) Facade

引进门面模式,设置一个接待员,又接待员提供各类服务
例如:医院可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。
这个接待员就是门面模式的体现,病人只接触接待员,由接待员与各个部门打交道。

  • 门面(Facade)角色:客户端可以调用这个角色的方法。
    此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。

  • 子系统(SubSystem)角色:可以同时有一个或者多个子系统。
    每个子系统都不是一个单独的类,而是一个类的集合(如上面的子系统就是由ModuleA、ModuleB、ModuleC三个类组合而成)。每个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ModuleA {  
//示意方法
public void testA(){
System.out.println("调用ModuleA中的testA方法");
}
}
public class ModuleB {
//示意方法
public void testB(){
System.out.println("调用ModuleB中的testB方法");
}
}
public class ModuleC {
//示意方法
public void testC(){
System.out.println("调用ModuleC中的testC方法");
}
}

public class Facade {
//示意方法,满足客户端需要的功能
public void test(){
ModuleA a = new ModuleA();
a.testA();
ModuleB b = new ModuleB();
b.testB();
ModuleC c = new ModuleC();
c.testC();
}
}


public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.test();
}
}

//Facade类其实相当于A、B、C模块的外观界面
//有了这个Facade类,那么客户端就不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在
//客户端只需要跟Facade类交互就好了,从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统。

优点

  • 松散耦合

门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。

  • 简单易用

门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。

  • 更好的划分访问层次

通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。
把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。

缺点

不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了”开闭原则”。

享元模式(共享模式) Flyweight

一个系统中如果有多个相同的对象,那么只共享一份就可以了
比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象。
如果有一个1M的文本,那么字母是何其的多,如果每个字母都定义一个对象那么内存早就爆了。
那么如果要是每个字母都共享一个对象,那么就大大节约了资源。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public abstract class Flyweight{  
public abstract void operation();
}

public class ConcreteFlyweight extends Flyweight{
private String string;
public ConcreteFlyweight(String str){
string = str;
}
public void operation(){
System.out.println("Concrete---Flyweight : " + string);
}
}


//工厂方法类-非线程安全
public class FlyweightFactory{
private Hashtable flyweights = new Hashtable();
public FlyweightFactory(){}
public Flyweight static getFlyWeight(Object obj){
  Flyweight flyweight = (Flyweight) flyweights.get(obj);
  if(flyweight == null){
//产生新的ConcreteFlyweight
flyweight = new ConcreteFlyweight((String)obj);
flyweights.put(obj, flyweight);
   }
return flyweight;
}
public int getFlyweightSize(){
return flyweights.size();
}
}


public class FlyweightPattern{
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1;
Flyweight fly2;
Flyweight fly3;
Flyweight fly4;
Flyweight fly5;
Flyweight fly6;
 
public FlyweightPattern(){
fly1 = factory.getFlyWeight("Google");
   fly2 = factory.getFlyWeight("Qutr");
   fly3 = factory.getFlyWeight("Google");
   fly4 = factory.getFlyWeight("Google");
   fly5 = factory.getFlyWeight("Google");
   fly6 = factory.getFlyWeight("Google");
  }
public void showFlyweight(){
  fly1.operation();
  fly2.operation();
  fly3.operation();
  fly4.operation();
  fly5.operation();
  fly6.operation();
  int objSize = factory.getFlyweightSize();
  System.out.println("objSize = " + objSize);
}
public static void main(String[] args){
  System.out.println("The FlyWeight Pattern!");
  FlyweightPattern fp = new FlyweightPattern();
  fp.showFlyweight();
}
}


/**
Concrete---Flyweight : Google
Concrete---Flyweight : Qutr
Concrete---Flyweight : Google
Concrete---Flyweight : Google
Concrete---Flyweight : Google
Concrete---Flyweight : Google
objSize = 2
*/

定义了6个对象,其中有5个是相同的
按照Flyweight模式的定义”Google”应该共享一个对象
在实际的对象数中我们可以看出实际的对象却是只有2个。

优点

享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

行为型模式

策略模式 Strategy

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public interface ICalculatorStrategy {  
public int operate(String exp);
}
//辅助抽象类
public abstract class AbstractCalculatorStrategy {
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}

public class Plus extends AbstractCalculatorStrategy implements ICalculatorStrategy {

@Override
public int operate(String exp) {
int arrayInt[] = split(exp,"\\+");
return arrayInt[0]+arrayInt[1];
}
}
public class Minus extends AbstractCalculatorStrategy implements ICalculatorStrategy {

@Override
public int operate(String exp) {
int arrayInt[] = split(exp,"-");
return arrayInt[0]-arrayInt[1];
}
}


public class Context {
private ICalculatorStrategy strategy;
//构造函数,要你使用哪个妙计
public Context(ICalculatorStrategy strategy){
this.strategy = strategy;
}
public void setStrategy(ICalculatorStrategy strategy){
this.strategy = strategy;
}
public void operate(){
this.strategy.operate();
}
}

//测试类
public class StrategyTest {
public static void main(String[] args) {
String exp = "2+8";
int result = 0;

Context context;
context = new Context(new Plus());
result = context.operate(exp);

context.setStrategy(new Minus());
result = context.operate(exp);
}
}

以上就是策略模式,多种不同解决方案(算法)动态切换,起到改变对象行为的效果。

优点

  • 策略模式提供了对”开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。

缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

模板模式 Abstract

模式中的角色

  • 抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
  • 具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public abstract class AbstractPerson{  
//抽象类定义整个流程骨架
public void prepareGotoSchool(){
dressUp();
eatBreakfast();
takeThings();
}
//以下是不同子类根据自身特性完成的具体步骤
protected abstract void dressUp();
protected abstract void eatBreakfast();
protected abstract void takeThings();
}

public class Student extends AbstractPerson{
@Override
protected void dressUp() {
System.out.println("穿校服");
}
@Override
protected void eatBreakfast() {
System.out.println("吃妈妈做好的早饭");
}
@Override
protected void takeThings() {
System.out.println("背书包,带上家庭作业和红领巾");
}
}

public class Teacher extends AbstractPerson{
@Override
protected void dressUp() {
System.out.println("穿工作服");
}
@Override
protected void eatBreakfast() {
System.out.println("做早饭,照顾孩子吃早饭");
}
@Override
protected void takeThings() {
System.out.println("带上昨晚准备的考卷");
}
}

public class Client {
public static void main(String[] args) {
Student student = new Student()
student.prepareGotoSchool();

Teacher teacher = new Teacher()
teacher.prepareGotoSchool();
}
}

优点

模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展。
通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合”开放-封闭原则”。

缺点

每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。

观察者模式 Observer/Observable & Watcher/Watched

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//抽象观察者角色类
interface Observer {
void update(Subject subject);
}
//具体观察者角色类
class ProgramMonkeyObserver implements Observer {
@Override
public void update(Subject subject) {
String state = ((SDKDownloadSubject)subject).getState();
System.out.println("Programer look the SDK download process is: "+state);
}
}
//抽象主题角色类
abstract class Subject {
private List<Observer> list = new ArrayList<>();

public void attach(Observer observer) {
list.add(observer);
}

public void detach(Observer observer) {
list.remove(observer);
}

public void motifyObservers() {
for (Observer obs : list) {
obs.update(this);
}
}
}
//具体主题角色类
class SDKDownloadSubject extends Subject {
private String mState;

public String getState() {
return mState;
}

public void netProcessChange(String data) {
mState = data;
this.motifyObservers();
}
}
//客户端
public class Main {
public static void main(String[] args) {
SDKDownloadSubject sdkDownloadSubject = new SDKDownloadSubject();
Observer observer = new ProgramMonkeyObserver();
sdkDownloadSubject.attach(observer);
sdkDownloadSubject.netProcessChange("1%");
sdkDownloadSubject.netProcessChange("51%");
sdkDownloadSubject.netProcessChange("100%");
}
}

优点

  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
  • 观察者模式支持广播通信。
  • 观察者模式符合”开闭原则”的要求。

缺点

  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。