Java 设计模式

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

设计模式六原则

  • 开闭原则(Open Close Principle)
    开闭原则就是说对扩展开放,对修改关闭
  • 里氏代换原则(Liskov Substitution Principle)
    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。氏代换原则是对“开-闭”原则的补充
  • 依赖倒转原则(Dependence Inversion Principle)
    这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
  • 接口隔离原则(Interface Segregation Principle)
    使用多个隔离的接口,比使用单个接口要好。降低类之间的耦合度
  • 迪米特法则(最少知道原则)(Demeter Principle)
    一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立
  • 合成复用原则(Composite Reuse Principle)
    原则是尽量使用合成/聚合的方式,而不是使用继

设计模式分类

总体来说设计模式分为三大类,GOF定义的是23种设计模式,实际上现在已经不止23种:

  • 创建型模式,共五种:(简单工厂)、工厂方法模式、抽象工厂模式、单例模式、构建者模式、原型模式
  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式、(空对象)
  • J2EE WEB模式, 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)、MVVM 模式(MVVM Pattern)

设计模式入门(策略模式)

鸭子应用类图

策略模式-鸭子应用

对于鸭子对象与行为,父类来控制游泳与呱呱叫行为直接写具体实现,外观显示display()方法抽象出来让每个鸭子自己继承后实现控制,扩展容易,需要鸭子飞的话直接让父类添加飞fly()即可。

从继承到接口,将飞fly()从父类抽取放进接口,不需要每次都检查功能覆盖,而且能清晰的看到所有鸭子的行为区别,然而重复的代码变多,代码无法复用。
但是39个鸭子都要改飞行怎么办?

采取良好的OO软件设计原则

  • 找出应用中需要变化(不通用)之处,把他们独立出来,不要和那些不需要变化(通用)的代码混在一起。
  • 针对接口编程,而不是针对实现编程
  • 从接口到封装隔离
    (FlyBehavior)隔离出一个鸭子行为类,去实现需要的行为接口,这样的设计,可以让动作被其它的类复用,因为这些行为和鸭子无关了。
  • 从隔离到抽象父类
    (Animal)将父类换成抽象类(可以是抽象类,也可以是接口),这样的意义在于,具体的实现不会被父类绑死,实例化动作不再需要硬编码,而是在运行时指定具体实现的对象,不要关心它具体是鸭子是狗,只要让它叫就可以。
  • 整合行为成为变量 将飞行和呱呱叫声明为接口类型的变量,不用鸭子对象不亲自处理呱呱叫的行为,而是委托给quackBehavior引用的对象,quackBehavior在运行时再通过实例化相应实现类来做出行为。
  • 行为变量从静态到动态
    在Duck类中添加set方法来随时改动行为变量,这样在运行时构造器实例化为不会飞的鸭子,也能动态改变为火箭动力的鸭子。
  • 鸭子到算法族
    HAS-A 优于 IS-A

多用组合(composition),少用继承
上述中的飞与呱呱叫行为的组合,使得系统具有很大的弹性,不仅封装算法族为类,还可以运行时改变行为。

策略模式(Strategy Pattern) 定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。其实就是把容易改动、变化的部分单独拿出来封装,使得这部分改变不影响其它代码,重要概念多态

共享模式词汇 共享模式词汇用更少的词汇做更充分的沟通,用大家都能理解的模式词汇易于沟通。

建立可维护的OO系统,在于随时想到系统可能需要的变化和应付变化的原则。
可复用
可扩充
可维护

不管当初软件设计得多好,一段时间后,总是需要成长与改变,否则软件就会“消亡”。

面向对象汇总工具箱

面向对象基础 OO Basics

  • 抽象(Abstraction)
  • 封装(Ecapsulation)
  • 多态(Polymorphism)
  • 继承(Inheritance)

设计原则 OO Principles 比理论上的6种多了3种

  • 封装变化 Encapsulate what varies
  • 多用组合少用继承Favor composition over inheritence
  • 针对接口编程Program to interfaces,not implementations
  • 为了交互对象之间的松耦合设计而努力!(Strive for loosely coupled designs between objects that interact.)
  • 类应该扩展开放,对修改关闭(Classes should be open for extension but closed for modification.)
  • 要依赖抽象,不要依赖具体类(Depend on abstractions,Do not depend on concrete classes.)
  • 最少知识原则:只和你的密友谈话(Only talk to your friends.)
  • 别打电话给(调用)我,我会打电话给(调用)你(Don’t call us,we’ll call you.)
  • 一个类应该只有一个引起变化的原因(A class should have only one reason to change.)

设计模式 OO Patterns

  • Strategy - defines a family of algorithms,encapsulates each one,and make them interchangeable.Strategy lets the algorithm vary independently from clients that use it

参考

创建型模式

创建型模式是处理对象创建的设计模式。创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。

在以下情况中,可以考虑应用创建型模式:

  • 一个系统需要和它的对象和产品的创建相互独立。
  • 一组相关的对象被设计为一起使用。
  • 隐藏一个类库的具体实现,仅暴露它们的接口。
  • 创建独立复杂对象的不同表示。
  • 一个类希望它的子类实现它所创建的对象。
  • 类的实例化在运行时才指定。
  • 一个类只能有一个实例,而且这个实例能在任何时候访问到。
  • 实例应该能在不修改的情况下具有可扩展性。

1. 简单工厂(Simple Factory)

说明

定义:在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。专门定义一个类负责创建其他类的实例,被创建的实例通常都具有共同的父类或接口。
意图:提供一个类,由它负责根据一定的条件创建某一具体类的实例
原则:工厂模式用到了单一职责原则和开闭原则

另一种叫法静态工厂方法(Static Factory Method)

工厂可以根据参数的不同返回不同的产品,这就是简单工厂模式

3个角色:

  1. Factory(工厂):核心部分,创建对象
  2. Product(抽象类产品):工厂类创建对象的父类
  3. ConcreteProduct(具体产品):它要实现抽象产品中声明的抽象方法

简单工厂模式的优点:通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。

简单工厂模式的缺点:由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利。

使用场景

Spring BeanFactory

类图

简单工厂(Simple 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
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
// Product
interface Fruits {
//采摘水果
String get();
}
// ConcreteProduct
class Apple implements Fruits {

@Override
public String get() {
System.out.println("采摘苹果");
return "采摘苹果";
}
}
// ConcreteProduct
class Banana implements Fruits {

@Override
public String get() {
System.out.println("采摘香蕉");
return "采摘香蕉";
}
}
// Factory
class Factory {
// 静态方法
public static Fruits getFruits(String name) {
if (name.equals("苹果")) {
return new Apple();
}else if (name.equals("香蕉")) {
return new Banana();
}else {
return null;
}
}
}
// 反射实现
// Factory
class Factory1 {

public static Fruits getFruits(Class c) {
Fruits fruits = null;
try {
fruits = (Fruits) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
System.out.println("不支持抽象类或接口");
} catch (IllegalAccessException e) {
e.printStackTrace();
System.out.println("没有足够权限,即不能访问私有对象");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("类不存在");
}
return fruits;
}
}
// Client
public class FactoryDemo {

public static void main(String[] args) {

Fruits fruits = Factory.getFruits("苹果");
if (null == fruits) {
System.out.println("生产不了您想要的水果");
}else {
fruits.get();
}
}
}

2. 工厂方法模式(Factory Method)

说明

允许一个类的实例化推迟到子类中进行

使用场景

Spring FactoryBean,spring与mabatis的结合(SqlSessionFactoryBean)SqlSessionFactoryBean.getObject()

类图

代码实现

抽象工厂模式中存在四种角色,分别是抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色。

3. 抽象工厂模式(Abstract Factory)

说明

抽象工厂是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂是指当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。
工厂模式中的每一个形态都是针对一定问题的解决方案,工厂方法针对的是多个产品系列结构;而抽象工厂模式针对的是多个产品族结构,一个产品族内有多个产品系列。

使用场景

抽象工厂模式的一个主要功能是它能够隔离要生成的具体产品类, 由于这些类的实际类名部被隐藏在工厂内部,因此客户端根本不需要关心如何对它们进行实例化的细节。每种设计模式都是针对特定问题的解决方案,而抽象工厂模式面临的问题则是当涉及到有多个产品等级结构寸,如何更好地进行软件体系结构的设计。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点
以下情况可以考虑使用抽象工厂模式:

  • 一个系统要独立于它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 需要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 提供一个产品类库,而只想显示它们的接口而不是实现时。

案例:QQ皮肤,一键换肤可以换掉很多很多图标的外观或主题,还有Windows更换主题,肯德基例子

类图

代码实现

参考

4. 单例模式(Singleton)

说明

保证一个类只有一个实例,并且提供对这个实例的全局访问方式
单例设计的优点:
1、控制资源的使用。
2、控制实例的产生,达到节省资源的目的。
3、作为通信媒介,资源共享。

使用场景

  • 工具类:
    FTPUtils(内部 synchronized (p) 对发送数据进行了加锁,所以可以使用的时候可以不用再加锁)
    HTTPUtils
    DateUtils
    IMUtils(比如对,创建群组,添加人员等业务层面的操作进行封装)
  • 配置读取
  • 日志应用
  • 数据库连接池,减低打开关闭损耗
  • 线程池
  • 操作系统文件系统
  • Spring的依赖注入默认是单例模式

类图

代码实现

扩展

单例与并发:
单例是实例化不销毁被重复使用,原型使用后就销毁。所以避免单例对象使用中进行修改自身变量。
为什么spring单例模式可以支持多线程并发访问?
1、spring单例模式是指,在内存中只实例化一个类的对象
2、类的变量有线程安全的问题,就是有get和set方法的类成员属性
3、执行单例对象的方法不会有线程安全的问题
为什么局部变量不会受多线程影响?
1、对于那些会以多线程运行的单例类,例如Web应用中的Servlet,每个方法中对局部变量的操作都是在线程自己独立的内存区域内完成的,所以是线程安全的
2、局部变量不会受多线程影响
3、成员变量会受到多线程影响
4、对于成员变量的操作,可以使用ThreadLocal来保证线程安全

单例多线程创建:
静态内部类实现

多线程并发调用静态方法:
多个线程调用静态方法,是否会出现并发问题取决于,静态方法内部是否需要引用共享区内的静态变量。当线程调用静态方法时,都会创建一套临时变量,可见性是在这个线程内部,所以当多个线程调用静态方法时,并且这个静态方法没有引用外部静态变量的。不会有线程并发的问题。也就是只要没有范围到会变化的静态全局的变量就不会有问题。如果存在全局变量,则需要使用同步机制

参考

5. 构建者模式(Builder)

说明

建造者模式的定义为:将一个复杂对象的构建和它的表示分离开,使得同样的构建过程可以创建不同的表示。

4个角色:

  1. 抽象建造者(Builder)角色:该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。
  2. 具体建造者(Concrete Builder)角色:该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。
  3. 产品(Product)角色:该角色是建造者中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。
  4. 导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder开始建造。

常见的构造方式:

  1. 多个重载的构造函数。优点:简单。缺点:参数多了不适用,不容易维护
  2. 通过setXX()方法构造。优点:容易理解。缺点:参数不能是final,对象set不连续,代码量多,最后可能构造的是不完整的对象

使用场景

类图

代码实现

builder模式例子变种1:

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
public class Person {
private final String name;
private int age;
private String location;
private String job;

public String getName() {
return name;
}

public int getAge() {
return age;
}

public String getLocation() {
return location;
}

public String getJob() {
return job;
}

Person(PersonBuilder personBuilder) {
this.name = personBuilder.name;
this.location = personBuilder.location;
this.job = personBuilder.job;
this.age = personBuilder.age;
}

public static class PersonBuilder {
final String name;
int age;
String location;
String job;

PersonBuilder(String userName) {
this.name = userName;
}

public PersonBuilder age(int age) {
this.age = age;
return this;
}

public PersonBuilder location(String location) {
this.location = location;
return this;
}

public PersonBuilder job(String job) {
this.job = job;
return this;
}

public Person builder() {
return new Person(this);
}
}

public static void main(String[] args) {
Person person = new Person.PersonBuilder("FF")
.age(20)
.job("CODING")
.location("CHINA")
.builder();
System.out.println(person.getAge());
}
}

可以看到变种的builder模式包括以下内容:

  1. 一个静态内部类,静态内部类的参数和构建类一样。
  2. 外部类只提供get方法方便查看,静态内部类提供set方法,赋值操作。
  3. 静态内部类提供的setter操作,返回值是当前Builder本身。
  4. 外部类的构造参数是静态内部类,使用静态内部类的变量赋值给外部类。
  5. 最终提供builder返回外部类

这种builder模式跟传统的builder模式确实是不太一样。但其实本质还是一样的,我们可以一一对应:

  • 产品(Product)角色:也就是创建一个类,声明其成员变量,相当于person类。
  • 抽象建造者角色:相当于静态内部类,复制产品定义的属性到静态内部类中,同时生成set方法。
  • 具体的建造者:也就是外部类提供的构造函数,将静态内部类的变量值赋值给外部类。
  • 导演角色:静态内部类中的builder方法。

优点: 看起来整齐。 先赋值后创建对象。 缺点: 需要编写额外的代码。

6. 原形模 式(Prototype)

说明

使用原型实例指定要创建的对象类型,通过复制原型创建新的对象

使用场景

类图

代码实现

7. 对象池模式

说明

对象池模式(The Object Pool Pattern)是单例模式的一个变种,它提供了获取一系列相同对象实例的入口。当你需要对象来代表一组可替代资源的时候就变的很有用,每个对象每次可以被一个组件使用。

使用场景

对象池模式经常用在频繁创建、销毁对象(并且对象创建、销毁开销很大)的场景,比如数据库连接池、线程池、任务队列池等。
业务应用:比如图书馆图书的借出归还

类图

代码实现

结构型模式

1. 适配器模式(Adapter)

说明

使用场景

SpringMVC中的适配器HandlerAdapter,根据Handler规则的执行不同的Handler,通过添加适配器类实现SpringMVC的扩展

类图

代码实现

2. 装饰器模式(Decorator)

说明

使用场景

Spring中类名含有Wrapper、Decorator。动态的添加一些额外的职责

类图

代码实现

3. 代理模式(Proxy)

说明

当客户端代码需要调用某个对象时,不关心是否准确得到该对象,只要一个能提供该功能的对象即可,我们就可以返回该对象的代理

使用场景

Spring AOP 动态代理

类图

代码实现

4. 外观模式\门面模式(Facade)

说明

使用场景

HibernateTemplate(JdbcTemplate)是SessionFactory、Session、Query等类的门面,当客户端程序需要执行持久化查询时,程序无需调用这些类,而是直接调用HibernateTemplate门面方法即可

类图

5. 桥接模式(Bridge)

说明

使用场景

Dao

类图

6. 组合模式(Composite)

7. 享元模式(Flyweight)

行为型模式

1. 策略模式(Strategy)

说明

使用场景

Hibernate Dialect
Spring Resource,Spring大量使用Resource访问底层资源实现有:UrlResource、FileSystemResource、ByteArrayResource等

类图

2. 模板方法模式(Template Method)

说明

父类定义骨架(调用哪些方法与顺序),某些特定方法由子类实现。实现代码复用

使用场景

Spring中模板方法与回调模式结合,Template,Spring几乎所有外接扩展都采用这种模式,JDBC\Hibernate集成:JdbcTemplate、StatementCallback,变化的东西通过回调传入JdbcTemplate

类图

3. 观察者模式(Observer)

说明

使用场景

JMS
Spring事件驱动模型还有listener实现ApplicationListenerApplicationEventApplicationContext

类图

4. 迭代模式(Iterator)

5. 责任链模式(Chain Of Responsibility)

6. 模板方法(Template Method)

7. 命令模式(Command)

说明

使用场景

Hibernate Callback接口

类图

8. 备忘录模式(Memento)

9. 状态模式(State)

10. 中介者模式(Mediator)

11. 解释器模式(Interpreter)

12. 空对象(Null)

J2EE WEB 模式

1. MVC 模式(MVC Pattern)

2. 业务代表模式(Business Delegate Pattern)

3. 组合实体模式(Composite Entity Pattern)

4. 数据访问对象模式(Data Access Object Pattern)

5. 前端控制器模式(Front Controller Pattern)

6. 拦截过滤器模式(Intercepting Filter Pattern)

7. 服务定位器模式(Service Locator Pattern)

8. 传输对象模式(Transfer Object Pattern)

9. MVVM模式(MVVM Patter)