策略模式、装饰模式、观察者模式、单例模式

微信图片_20211007174823

设计模式

设计模式

策略者模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

开闭原则

对修改关闭、对扩展开放、操作者不能修改我们的代码、但是可以自己去扩展添加自己想要的功能

案例

  • Operation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author xiaobo
* @date 2021/10/7 - 14:11
* 定义一个操作接口
*/
public interface Operation {

/**
* 执行操作的接口方法
* @param num1 参数1
* @param num2 参数2
* @return 返回计算结果
*/
public int doOperation(int num1, int num2);

}
  • Calculator
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
/**
* @author xiaobo
* @date 2021/10/7 - 14:12
* 实现操作接口、使其操作这个计算器类即可
*/
public class Calculator implements Operation{

//将操作接口注入进来
private Operation operation;

/**
* 生成一个set方法
* @param operation
*/
public void setOperation(Operation operation) {
this.operation = operation;
}

/**
* 实现的接口方法
* @param num1 参数1
* @param num2 参数2
* @return
*/
@Override
public int doOperation(int num1, int num2) {
//返回接口中所返回的值
return this.operation.doOperation(num1, num2);
}

}
  • OperationAdd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author xiaobo
* @date 2021/10/7 - 14:17
* 加法操作、实现操作接口
*/
public class OperationAdd implements Operation{

/**
* 实现操作接口并将其变为加法操作方法
* @param num1 参数1
* @param num2 参数2
* @return
*/
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
  • OperationSub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author xiaobo
* @date 2021/10/7 - 14:19
* 减法操作、实现操作接口
*/
public class OperationSub implements Operation {

/**
* 实现操作接口并将其变为减法操作方法
* @param num1 参数1
* @param num2 参数2
* @return
*/
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
  • main
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
/**
* @author xiaobo
* @date 2021/10/7 - 14:19
* 测试类
*/
public class Test {

/**
* 主方法
* @param args
*/
public static void main(String[] args) {

//实例化计算器对象
Calculator calculator = new Calculator();

//实例化加法运算对象、并将其注入到计算器对象中
calculator.setOperation(new OperationAdd());

//进行加法运算
System.out.println("加法运算结果为 " + calculator.doOperation(2, 1));

//实例化减法运算对象、并将其注入到计算器对象中
calculator.setOperation(new OperationSub());

//进行加法运算
System.out.println("减法运算结果为 " + calculator.doOperation(2, 1));
}
}

策略模式结果

装饰模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成”庙宇”后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

案例

  • person
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author xiaobo
* @date 2021/10/7 - 15:20
* 定义人的接口
*/
public interface Person {

/**
* 消费金额
* @return
*/
public BigDecimal cost();

/**
* 显示信息
*/
public void show();
}
  • XiaoBo
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
/**
* @author xiaobo
* @date 2021/10/7 - 15:23
* 被装饰类
*/
public class XiaoBo implements Person {

/**
* 实现接口的消费金额方法、初始化为0
* @return
*/
@Override
public BigDecimal cost() {

return new BigDecimal(0.0);
}

/**
* 实现显示信息的接口
*/
@Override
public void show() {
System.out.println("啥也没有的小博");
}
}
  • ClothesDecorator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author xiaobo
* @date 2021/10/7 - 15:25
* 装饰类
*/
public abstract class ClothesDecorator implements Person{

//将Person接口注入进来
protected Person person;

/**
* 构造方法、初始化
* @param person
*/
public ClothesDecorator(Person person) {
this.person = person;
}
}
  • Smoke
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
/**
* @author xiaobo
* @date 2021/10/7 - 15:28
* 买东西的类
*/
public class Smoke extends ClothesDecorator{

/**
* 继承父类的构造方法
* @param person
*/
public Smoke(Person person) {
super(person);
}

/**
* 累加消费金额
* @return
*/
@Override
public BigDecimal cost() {

return this.person.cost().add(BigDecimal.valueOf(11.0));
}

/**
* 显示消息
*/
@Override
public void show() {
this.person.show();
System.out.println("买了一包烟、累积消费" + this.cost() + "元");
}
}
  • Lighter
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
/**
* @author xiaobo
* @date 2021/10/7 - 15:38
* 买东西的类
*/
public class Lighter extends ClothesDecorator {

/**
* 继承父类的构造方法
* @param person
*/
public Lighter(Person person) {
super(person);
}

/**
* 累加消费金额
* @return
*/
@Override
public BigDecimal cost() {
return this.person.cost().add(BigDecimal.valueOf(3.0));
}

/**
* 显示消息
*/
@Override
public void show() {
this.person.show();
System.out.println("买了个火机、累积消费" + this.cost() + "元");
}
}
  • main
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
/**
* @author xiaobo
* @date 2021/10/7 - 15:40
* 测试类
*/
public class Test {

/**
* 主方法
* @param args
*/
public static void main(String[] args) {

//实例化被装饰的对象
Person xiaobo = new XiaoBo();

//告诉商品谁买了我
xiaobo = new Smoke(xiaobo);

//告诉商品谁买了我
xiaobo = new Lighter(xiaobo);

//显示信息
xiaobo.show();
System.out.println("小博总消费 " + xiaobo.cost() + "元");
}
}

装饰模式结果

观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决:使用面向对象技术,可以将这种依赖关系弱化。

关键代码:在抽象类里有一个 ArrayList 存放观察者们。

应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

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

使用场景:

一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

案例

  • Customer
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author xiaobo
* @date 2021/10/7 - 16:55
* 客户抽象类
*/
public abstract class Customer {

/**
* 抽象方法、提高扩展性
*/
public abstract void update();
}
  • CustomerA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author xiaobo
* @date 2021/10/7 - 16:56
* 观察者类
*/
public class CustomerA extends Customer {

/**
* 重写该方法
*/
@Override
public void update() {
System.out.println("客户A的报纸已送达");
}
}
  • CustomerB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author xiaobo
* @date 2021/10/7 - 16:57
* 观察者类
*/
public class CustomerB extends Customer{

/**
* 重写该方法
*/
@Override
public void update() {
System.out.println("客户B的报纸已送达");
}
}
  • newSpaperOffice
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
/**
* @author xiaobo
* @date 2021/10/7 - 16:58
* 被观察者类
*/
public class NewSpaperOffice {

//定义一个List集合、用于存放客户
List<Customer> customers = new ArrayList<>();

/**
* 将客户添加到集合中的方法
* @param customer
*/
public void addCustomer(Customer customer){
customers.add(customer);
}

/**
* 循环遍历集合、依次调用update方法
*/
public void notifyAllObserver(){
for (Customer customer : customers){
customer.update();
}
}

/**
* 开始派发方法
*/
public void newSpaper(){
this.notifyAllObserver();
}
}
  • main
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
/**
* @author xiaobo
* @date 2021/10/7 - 17:02
* 测试类
*/
public class Test {

/**
* 主方法
* @param args
*/
public static void main(String[] args) {

//创建报社实例化对象
NewSpaperOffice newSpaperOffice = new NewSpaperOffice();

//添加要派发的客户
newSpaperOffice.addCustomer(new CustomerA());
//添加要派发的客户
newSpaperOffice.addCustomer(new CustomerB());

//开始派发
newSpaperOffice.newSpaper();
}
}

观察者模式结果

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

应用实例:

1、一个班级只有一个班主任。
2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

案例

单线程下的单例模式

  • Single
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
/**
* @author xiaobo
* @date 2021/10/7 - 17:22
* 单例类
*/
public class Single {

//定义一个静态私有的Single属性
private static Single instance;

//设置私有的构造犯法
private Single(){
System.out.println("创建了Single对象");
}

//实例化对象
public static Single getInstance(){

//当instance为空时
if(null == instance){
//创建实例化对象
instance = new Single();
}
//返回
return instance;
}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author xiaobo
* @date 2021/10/7 - 17:25
*/
public class Test {

public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Single.getInstance();
}
}
}

单例模式结果

多线程

  • single
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
/**
* @author xiaobo
* @date 2021/10/7 - 17:22
* 单例类
*/
public class Single {

//定义一个静态私有的Single属性
private volatile static Single instance;

//设置私有的构造犯法
private Single(){
System.out.println("创建了Single对象");
}

//实例化对象
public static synchronized Single getInstance(){

//当instance为空时
if(null == instance){
synchronized (Single.class){
if(null == instance){
//创建实例化对象
instance = new Single();
}
}
}
//返回
return instance;
}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @author xiaobo
* @date 2021/10/7 - 17:25
*/
public class Test {

public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
Single.getInstance();
}).start();
}
}
}

单例模式结果

正确的开始、微小的长进、然后持续、嘿、我是小博、带你一起看我目之所及的世界……

-------------本文结束 感谢您的阅读-------------

本文标题:策略模式、装饰模式、观察者模式、单例模式

文章作者:小博

发布时间:2021年10月07日 - 17:55

最后更新:2021年10月07日 - 17:56

原始链接:https://codexiaobo.github.io/posts/2677239693/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。