设计模式的总览与分类
导语
在之前完成基于UDP的通信项目时,用到了设计模式中的单例模式。在写事件的监听器时,也了解到了观察者模式。
于是乎,我对于设计模式想要有一个系统的了解,也希望能够实际运用好合适的设计模式,来实现代码的低耦合、高复用。
设计模式是什么
首先,什么是设计模式呢?
它是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结,相当于某种约定俗成的规则。使用设计模式可以令代码真正地实现工程化。
项目中,合理运用设计模式可以完美地解决很多问题。因为每种设计模式都是为了解决我们经常遇到的某种问题,也有实际的原理与之对应。
我们之所以提倡使用设计模式,是为了实现代码复用,增加可维护性。
设计模式的六大原则
设计模式的原则其实来源于面向对象编程的原则,总的来说,是:
对接口编程而不是对实现编程。
优先使用对象组合而不是继承。
开闭原则
开闭原则(Open Close Principle),是指对扩展开放,对修改关闭。
意思是说,在代码需要扩展时,能够不修改原有的代码。
要实现这一原则,关键的步骤就是抽象化。接口和抽象类的使用可以帮助我们实现抽象化。
比如:我们可以编写student接口,包含study的接口方法。
而当我们实际使用时,如果需要大学生,就直接写一个大学生类college_student实现student接口,就可以进行扩展;需要小学生,就写一个小学生类pupil来实现student接口。而不必修改之前写好的代码。
里氏代换原则
里氏代换原则(Liskov Substitution Principle)是指,任何子类都可以出现在其基类出现的地方,也可以替换掉基类,并且软件单元的功能不受影响。
这是对开闭原则的补充。因为开闭原则是说要实现抽象化,而里氏代换则是指出了实现抽象化的具体步骤的规范。
所以要求:
- 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
通俗一点说,里氏代换原则要求子类比父类更加宽松。
依赖倒转原则
依赖倒转原则(Dependence Inversion Principle)的定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。核心思想是指:针对接口编程,依赖于抽象而不依赖于具体。
比如顾客类customer有一个shopping方法,该方法的形参是youyishop类的对象,表示该顾客去友谊商店购物。
如果我们想修改需求,要表示该顾客去步步高商城购物,就要修改customer类的shopping方法的形参,改为bubugaoshop类的对象。
这显然是不符合我们面向对象设计的原则的。
.
所以在编写customer类的shopping方法时,不应该让它依赖youyishop类或者bubugaoshop类,而应该让它依赖shop接口。
这样,我们在后续的商店变动过程中,就不需要修改原有的代码,只需要增加新的代码去实现shop接口。
接口隔离原则
接口隔离原则(Interface Segregation Principle)和面向对象编程中的单一职责原则是相辅相成的。也就是说,使用多个隔离的接口,比使用单个含有不同接口方法的接口要好。
所以,我们要做的就是 为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
这样一来,当一个类实现接口时,就不用实现其不需要的接口方法。
这样可以降低类之间的耦合度。
尽管接口隔离原则和单一职责原则都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
- 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
- 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
迪米特原则
迪米特原则(Demeter Principle)也叫最小知道原则,是指一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相互独立。
其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
比如说我在之前写UDP通信的项目时,应该将收发消息的通信模块和处理消息的模块分隔开,不应直接互相调用。这样比较便于代码的修改与维护。
下图也是一个很好的例子。明星和粉丝、媒体公司之间尽量少地相互作用,而是通过经纪人进行处理。
合成复用原则
合成复用原则(Composite Reuse Principle)是指:尽量使用合成/聚合的方式,而不是使用继承。
这是基于类的复用来讲的。
我们知道类的复用一般分为继承复用和合成复用两种:
继承复用:
- 简单、易实现
- 破坏类的封装性(将父类细节暴露给了子类),又叫“白箱”复用。
- 父类、子类之间耦合度高,父类的任何改变也会导致子类的改变,不利于类的拓展和维护
- 限制了复用的灵活性。因为继承是静态的,编译时已经定义。
合成复用:
- 维持了类的封装性,又叫“黑箱”复用。
- 新旧类之间耦合度低。复用所需依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
- 复用的灵活性高。可以在运行时动态进行,新对象可以动态引用与成分对象类型相同的对象。
继承复用的例子:
合成复用的例子:
23种经典设计模式
创建型模式(5种)
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
- 工厂模式(Factory Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
结构型模式(7种/8种)
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 过滤器模式(Filter、Criteria Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
行为型模式(11种/12种)
这些设计模式特别关注对象之间的通信。
- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 空对象模式(Null Object Pattern)
- 策略模式(Strategy Pattern)
- 模板模式(Template Pattern)
- 访问者模式(Visitor Pattern)
J2EE模式(8种)
这些设计模式特别关注表现层。这些模式是由Sun Java Center鉴定的。
- 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)
结束语
本篇博客主要记录了自己对设计模式的六大原则的理解。
依我之见,设计模式其实是一些降低代码耦合度、提高代码复用性的方案。而六大原则的目的也是如此。
开闭原则告诉我们要对扩展开放,对修改关闭;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;单一职责原则告诉我们实现类要职责单一;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合度;合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。
而对于23种设计模式,仅仅是列举了一下。将会在以后的学习中深入理解,并且总结成学习博客。