设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four(GoF)的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式。
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一类设计模式:J2EE 设计模式。
序号 | 模式 & 描述 | 设计模式 |
1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活 |
|
2 | 结构型模式 这些模式关注对象之间的组合和关系,旨在解决如何构建灵活且可复用的类和对象结构。 |
|
3 | 行为型模式 这些模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。 |
|
UML,全称为Unified Model Language,即统一建模语言,是由一整套图表组成的,为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言。UML 代表了一组最佳工程实践,这些实践已被证明在大型复杂系统的建模中是成功的。UML是开发面向对象软件和软件开发过程中非常重要的一部分。在嵌入式系统设计中,使用UML建模并书写文档,通常可以起到事半功倍的效果。
UML是在开发阶段,说明、可视化、构建和书写一个面向对象软件密集系统的制品的开放方法。最佳的应用是工程实践,对大规模,复杂系统进行建模方面,特别是在软件架构层次,已经被验证有效。统一建模语言(UML)是一种模型化语言。模型大多以图表的方式表现出来。一份典型的建模图表通常包含几个块或框,连接线和作为模型附加信息之用的文本。这些虽简单却非常重要,在UML规则中相互联系和扩展。
UML具有许多不同类型的图表,包括:
这些不同的图,可以提供从不同的角度来描述系统,因为大型的软件开发流程中除了程序员外,还有产品、设计、测试等人员,这些人都对系统的不同方面有不同关注,因此在建模时需要考虑不同的细节层次。
设计思路
"既要也要",
设计一个类之间的关系既要高内聚,
设计多个类之间的关系要低耦合
常用的类之间关系
依赖关系
泛化关系
实现关系
聚合关系
组合关系
public class Student{
private Course course
public Student(Course course) {
this.course = course;
}
public void study (){
Course.learn();
}
}
已上代码依赖于 Course类
常规用法在泛型
MutiOverClass
//MutiOverClass:泛型类名称
public class OverClass {
private T over;
public T getOver() {
return over;
}
public void setOver(T over) {
this.over = over;
}
public static void main(String args[]) {
OverClass over1 = new OverClass();
OverClass over2 = new OverClass();
over1.setOver(true);
over2.setOver(3.14f);
Boolean b = over1.getOver();
Float f = over2.getOver();
System.out.println(b);
System.out.println(f);
}
}
/*输出结果如下:
true
3.14
*/
public class Engine
{
}
public class Wheel
{
}
public class Light
{
}
public class Car {
private Engine engine;
private Light light;
private Wheel wheel;
public void setEngine(Engine engine) {
this.engine = engine;
}
public void setLight(Light light) {
this.light = light;
}
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
public void drive()
{
}
}
public class Mouth
{
}
public class Nose
{
}
public class Head
{
private Mouth mouth;
private Nose nose;
public Head()
{
mouth = new Mouth();
nose = new Nose();
}
public void shake()
{
}
}
一个类被改变的原因不能超过一个,也就是说,一个类只有一个职责,如果职责过多,代码就会臃肿,可读性更差,也更难以维护。其实上单一职责原则和接口隔离原则有一定的关系,接口隔离以后,职责就单一了,实现这个接口的类的职责自然也就单一了。但是接口隔离关注的是抽象层,单一职责关注的是两者兼而有之,偏重于实现。
客户端不应该被迫依赖于它不使用的方法(Clients should not be forced to depend on methods they do not use)。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。High level modules should not depend upon low level modulesboth should depend upon abstractions. Abstractions should not depend upon detailsdetails
should depend upon abstractions.面向接口编程从而解耦
注意:
1)底层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
2)变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓存层,利于程序扩展和优化
3)继承时遵循里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出来的,她提出:继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。
里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。
开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则。
一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
如果一个系统满足迪米特法则,那么当其中一个软件实体发生变化时,就会尽量少的影响其他软件实体,扩展会相对容易,这是对软件实体之间通信的限制,迪米特法则要求限制软件实体之间通信的宽度和深度。迪米特法则可以降低系统的耦合度,使类与类之间保持松耦合状态。
迪米特法则还有几种定义形式,包括:不要和"陌生人"说话、只与你的直接朋友通信等,在迪米特法则中,对于一个对象,其朋友包括以下几类:
任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。在应用迪米特法则时,一个对象只能与直接朋友发生交互,不能与“陌生人”发生直接交互,这样子可以降低系统的耦合度,一个对象的改变不会给太多其他对象带来影响。
迪米特法则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象不必直接通信,那么这两个对象就不应该发生任何直接的相互作用,如果其中一个对象需要调用另外一个对象的某个方法时,可以通过第三者转发这个调用。就是通过引入一个合理的第三者来降低先有对象之间的耦合度。
合成复用原则(Composite Reuse Principle, CRP):尽量使用对象组合,而不是继承来达到复用的目的。
处理前:
处理后:
当需要应对变化的时候,应该首先使用组合的方式,而不是继承——因为组合更加灵活