YNZH's Blog

常用经典设计模式~

一个有用的站点

七大设计原则

  • 单一职责原则:尽量保持类或者方法的功能单一,提高代码可读性、复用性
  • 接口隔离原则:在设计接口要注意不要有多余的方法,尽量使用精简的小接口、避免冗余
  • 依赖倒转原则:尽可能的依赖高层的抽象的接口、合理利用多态特性,避免依赖具体的某个子类我,提升代码的灵活性
  • 里氏替换原则:子类尽量避免重写父类中已经实现的方法
  • 开闭原则 :类或者方法对扩展是开放的对修改是闭合的,在不更改代码的前提下通过扩展实现新的功能减少bug,通过扩展实现变化
  • 迪米特法则 :如果两个模块需要通信,为减少两者之间的耦合性,尽可能的减少中间介质对象的创建,充分利用各个模块的独立性
  • 合成复用原则:软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

常用设计模式

策略模式

当一个方法可能有很多种实现的策略或者方式,可以使用策略模式将不同的策略抽象为一个接口,然后将实现不同策略的接口注入到需要包含该方法的类或者接口中,来实现同一个方法的不同策略。例如java中的Arrays.sort方法可以通过一实现Comparator接口来实现不同的比较策略.

工厂模式

为什么会产生工厂模式?

工厂模式分为简单工厂模式和工厂方法模式,想象一下当我们的一个类中要根据不同的条件去生成很多不同的对象(同一类型的不同对象)时候,是不是会有很多复杂的if_else语句块,更可怕的是当每隔对象的创建逻辑都很复杂而不是简单的new Object()的时候,代码就会变的及其难看,而且繁琐。这种情况下工程模式就应运而生了,我们可以把这一大段创建对象的代码封装到一个ObjectFactory中,这样有什么好处呢

  1. 对于调用者来说这需要一行代码即可完成对象创建,其他的不用管了!让工厂类去做吧。
  2. 工厂类可以复用,降低代码冗余。
  3. 代码可读性更高,类之间这则单一。

简单工厂模式

简单工厂模式就是通过一个工厂类完成各种对象的创建。

工厂模式

工厂模式在简单工厂模式的基础上,每种不同对象由不同的工厂类来实现,然后各个类别的对象工厂类是一个大工厂类的子类,调用方利用多态的形式去调用。这样情况适用于各个类别对象的创建特别复杂的时候,相当于在简单工厂方法中再进行一次抽象。

装饰者模式

为什么会有装饰者模式?

装饰者模式顾名思义就是对某个东西进行装饰,例如在java的IO类包设计中就使用了装饰着模式,首先我们看下图

1
2
3
4
5
graph TD
A[InputStream] -->A1(FileInputStream)
A[InputStream] -->A2(PipedInputStream)
A[InputStream] -->A3(ObjectInputStream)
A[InputStream] -->A4(ByteArrayInputStream)

现在我们要继承InputStream抽象类分别是实现不同的子类功能,但是我们都知道从磁盘中读取字符流是很耗时的,如果我们使用一个缓存区将要读的字节流加载到内存中,然后再从内存中读取就会快很多,如果读完了再从磁盘渠道缓冲区就行了。那我们就应该在每个实现了的子类中添加一个缓存读取的功能,此外要是还有其他功能例如直接从字符流中读某个类型的值比如Int、String等等,我们是不是应该在每个实现子类中添加相应的功能,如果我们通过继承的方式来实现会怎么样呢如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
graph LR
A[InputStream] -->A1(FileInputStream)
A[InputStream] -->A2(PipedInputStream)
A[InputStream] -->A3(ObjectInputStream)
A[InputStream] -->A4(ByteArrayInputStream)
A1(FileInputStream) --> A1_1(BufferedFileInputStream)
A1(FileInputStream) --> A1_2(DataFileInputStream)
A2(PipedInputStream) --> A2_1(BufferedPipedInputStream)
A2(PipedInputStream) --> A2_2(DataPipedInputStream)
A3(ObjectInputStream) -->A3_1(BufferedObjectInputStream)
A3(ObjectInputStream) -->A3_2(DataObjectInputStream)
A4(ByteArrayInputStream)-->A4_1(BufferedByteArrayInputStream)
A4(ByteArrayInputStream)-->A4_2(DataByteArrayInputStream)

可以看到每个子类都新加了两个子类来实现,那如果我们要加n个功能,子类个数将会增加很多甚至爆炸。

装饰者模式在JavaIO类中的应用

这时候装饰者模式就可以排上用场了。我们注意到增加Buffer功能的逻辑对于FileInputStream、PipedInputStream、ObjectInputStream、ByteArrayInputStream来说是一样的完全可以抽离出来复用啊!所以我们可以设计下面这种类图

1
2
3
4
5
6
7
graph TD
A[InputStream] -->A1(FileInputStream)
A[InputStream] -->A2(PipedInputStream)
A[InputStream] -->A3(ObjectInputStream)
A[InputStream] -->A4(ByteArrayInputStream)
A[InputStream] -->A5(BufferedInputStream)
A[InputStream] -->A6(DataInputStream)

我们可以新增两个InputStream的子类BufferedInputStream、DataInputStream类作为装饰类,比如说BufferedInputStream来说作为装饰类,那么要装饰谁呢,就应该持有一个成员变量:被装饰类(也就是前面的FileInputStream、PipedInputStream、ObjectInputStream、ByteArrayInputStream)然后借助被装饰者的read()实现自己的通用 的buffer_read()读取功能。对于DataInputStream来说也是同样的道理。借助被装饰类的基本功能完成自己的装饰功能。但是这样做还可以有一点改进就是新增的BufferedInputStream、DataInputStream类会有许多冗余的代码,也就是除了自己装饰功能的函数,因为自己和被装饰类继承子同一个抽象父类,所以装饰类实现抽象父类方法会委托被装饰类来完成。而这部分代码是完全一样的所以我们将这部分代码封装到一个装饰类父类中,每个装饰类只完成自己该有的职责。所以就会有下图这样的结构:

1
2
3
4
5
6
7
8
graph TD
A[InputStream] -->A1(FileInputStream)
A[InputStream] -->A2(PipedInputStream)
A[InputStream] -->A3(ObjectInputStream)
A[InputStream] -->A4(ByteArrayInputStream)
A[InputStream] -->A5(FilterInputStream)
A5[FilterInputStream] -->A5_1(BufferedInputStream)
A5[FilterInputStream] -->A5_2(DataInputStream)

通过这样的设计就可以减少子类爆炸的问题。减少继承通过装饰类来代替大量的继承子类。

迭代器模式

用来实现统一的集合类的迭代方式,屏蔽各种不同集合底层的实现。方便用户使用。

责任链模式

有点像链表,每一个节点单一的负责自己的职责,同时向下传递请求。由系统维护一个链表。

1
2
3
4
interface Handler{
void handle(request req);
void setNextHandler(Handler handler);
}

优点

  1. 单一职责原则。 你可对发起操作和执行操作的类进行解耦。
  2. 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者

观察者模式

类似发布者、订阅者的模式、又或者事件驱动模式。

情景:实体店Store不定期会有新品上市,不同类型的客户想要第一时间知道是否有新品到货。怎么做到呢?要么客户不停的往返实体店查看,要么实体店有新货就通知客户。就类似于客户订阅了实体店这个频道,实体店每次发布新品就会通知客户。

1
2
3
4
5
6
7
8
9
10
11
12
public class Store{
Map<EventType,Customer> listeners;
void subscribe(EventType et,Customer listener){
listener.put(et, listener);
}
void unsubscribe(EventType et,Customer listener){
listener.remove(et, listener);
}
void notify(){
foreach(Customer listener:listeners)
}
}

 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 , 总访问量为 次 。
载入天数...载入时分秒...