目录
1.基本概念
2.设计模式分类
2.1创建型模式(5种)
2.2结构型模式(7种)
2.3行为型模式(11种)
3.UML图
3.1基本概念
3.2UML分类(9种)
4.类之间的关系(8种)
4.1关联关系(3种)
4.1.1单向关联
4.1.2双向关联
4.1.3自关联
4.2聚合关系
4.3组合关系
4.4依赖关系
4.5继承(泛化)关系
编辑
4.6实现关系
5.软件设计原则(7种)
5.1(开闭)开放-关闭原则 (Open-Closed Principle)
5.1.1核心思想
5.1.2代码实现
5.1.3开放-关闭原则作用
5.2里氏替换原则 (Liskov Substitution Principle)
5.2.1核心思想
5.2.2代码实现
5.2.3里氏替换原则作用
5.3依赖倒转原则 (Dependence Inversion Principle)
5.3.1核心思想
5.3.2代码实现
5.3.3依赖倒转原则作用
5.4接口隔离原则 (Interface Segregation Principle)
5.4.1核心思想
5.4.2代码实现
5.4.3接口隔离原则作用
5.5迪米特原则(Law Of Demeter)
5.5.1核心思想
5.5.2代码实现
5.5.3迪米特原则作用
5.6(合成)组合/聚合复用原则 (Composite/Aggregate Reuse Principle)
5.6.1核心思想
5.6.2代码实现
5.6.3合成原则作用
5.7单一职责原则 (Single Responsibility Principle)
5.7.1核心思想
5.7.2代码实现
5.7.3单一职责原则作用
软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF(四人组)提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
用于描述如何将类或对象按某种布局组成更大的结构,GoF(四人组)提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF(四人组)提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
统一建模语言(Unified Modeling Language,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。
UML 从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等 9 种图。
在UML类图中,类使用包含类名、属性(field) 和方法(method) 且带有分割线的矩形来表示
属性/方法名称前加的加号和减号表示了这个属性/方法的可见性,UML类图中表示可见性的符号有三种:
+:表示public
-:表示private
#:表示protected
属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]
方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]
注意
中括号中的内容表示是可选的
将类型放在变量名前面,返回值类型放在方法名前面
关联关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系
关联又可以分为单向关联、双向关联、自关联
在UML类图中单向关联用一个带箭头的实线表示
单向关联:Customer类中的某个属性指向了Address类,实现单向关联
在UML类图中双向关联用一个不带箭头的直线表示
双向关联:Customer类中的某个属性指向了Product类,Product类中的某个属性也对应的指向了Customer类
在UML类图中自关联用一个带有箭头且指向自身的线表示
自关联:Node类自身的某个属性指向了自身,以此形成自关联
聚合关系是关联关系的一种,是强关联关系,是整体和部分之间的关系
聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在
在 UML 类图中聚合关系可以用带空心菱形的实线来表示,菱形指向整体
聚合关系:分整体对象University和部分对象Teacher,整体对象包含部分对象,部分对象离开了整体对象就无法单独存在
组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系
在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。例如,头和嘴的关系,没有了头,嘴也就不存在了
在 UML 类图中组合关系用带实心菱形的实线来表示,菱形指向整体
组合关系:分整体对象和部分对象,整体对象包含部分对象,部分对象离开了整体对象就无法单独存在
依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联
在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。
在 UML 类图中依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类
依赖关系:分主要对象和其他对象,其中主要对象的某个方法能调用其他对象的某个属性/方法
继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。
在 UML 类图中泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类
继承关系 / 泛化关系:分为父类和子类,多个子类继承父类,子类指向父类实现继承关系
实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作
在 UML 类图中实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口,接口需要用<
实现关系:接口是抽象出来的某种类对象,实现类对象想要与接口类对象建立实现关系,就必须要实现接口类对象中所声明的抽象操作
开闭原则是主张类 / 模块应该对扩展开放,对修改关闭。通俗的来说就是当需求发生变化时,我们应该尽可能的通过扩展现有代码来实现新的功能,而不是直接修改已有的类和模块。
扩展开放,修改关闭
有一个生产电脑的工厂可以生产小米电脑和华为电脑,根据电脑工厂的产线功能可以生产小米电脑和华为电脑,当我们需要对产线功能扩展时,完全可以再建立一个类用于实现Computer接口,既不影响原本的功能,还能扩展原本的产线功能。
interface Computer {}
public interface Factory {
//创建方法返回类型为Computer类型
Computer produceComputer();
}
class XiaoMi implements Computer{}
class HuaWei implements Computer{}
class XiaoMiFactory implements Factory {
public Computer produceComputer() {
return new XiaoMi();
}
}
class HuaWeiFactory implements Factory {
public Computer produceComputer() {
return new HuaWei();
}
}
能够扩展已存在的程序,并提供新的功能满足新的需求
提高代码的可维护性和可读性,并降低代码更引起的风险
里氏替换原则是实现开闭原则的重要方式之一,在使用父类对象的地方都可以使用子类对象,因此在程序中尽量使用父类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。通俗的来说子类可以扩展父类的功能,但不能改变父类原有的功能。
如果父类有一个非抽象方法,它保证在执行某些操作之前先检查一些条件。如果子类修改了这个方法,并且破坏了这些条件检查,那么就可能会导致程序出现错误或者异常。这种情况就会破坏整个继承体系的一致性和稳定性。
只扩展父类的功能,不修改父类原有功能
有一个类A实现了两数相加方法,有类B需要实现两数相减,且在两数相减的基础上再+3,这里类B继承了类A,但是我们不修改原有的继承的方法,而是选择扩展类A的功能完成扩展。
public class Calc {
public static void main(String[] args) {
B b = new B();
System.out.println(b.add(1,2));
}
}
class A{
public int sub(int a,int b){
return a-b;
}
}
class B extends A{
public int sub(int a,int b){
return a-b;
}
public int add(int a,int b){
return sub(a,b)+3;
}
}
代码共享,减少创建类的工作量,每个子类都拥有父类的方法或属性
提高程序的开放性和可扩展性
依赖倒转原则是要面向接口编程,而不是面向实现编程,高层模块不应该依赖低层模块,二者都应该依赖其抽象(不要依赖具体子类),也就是说抽象不应该依赖于细节,细节应当依赖于抽象。通俗的来说一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。
要依赖抽象,不要依赖具体
封装数据库操作可认为低层模块,而处理业务逻辑可认为高层模块,则处理业务逻辑的程序员提供一个封装好数据库操作的抽象接口,交给低层模块的程序员去编写,这样双方可以单独编写而互不影响
定义读接口完成读纸质读物,定义报纸类和故事类,定义mother对象完成给女儿讲故事,当需要读报纸时,则让报纸类实现读接口的功能,当需要读故事时,则让故事类实现读接口的功能。
public class Client {
public static void main(String[] args) {
Mother mother = new Mother();
mother.say(new NewsPaper());
mother.say(new Book());
}
}
interface IReader {
public String getContent();
}
class NewsPaper implements IReader{
public String getContent() {
return "这是一则重要的新闻";
}
}
class Book implements IReader{
public String getContent() {
return "这是一个有趣的故事";
}
}
class Mother{
public void say(IReader reader){
System.out.println("妈妈开始讲故事");
System.out.println(reader.getContent());
}
}
降低类之间的耦合性,提高系统的稳定性
减少并行开发引起的风险,提高代码的可读性和可维护性
接口隔离原则是建立单一接口,而不是建立庞大的接口,尽量细化接口,接口中的方法尽量少。通俗的来说,应该为各个类建立专用的接口,而不是建立庞大的接口供所有依赖它的类去调用
建立单一接口,细化接口
定义动物接口IAnimal、定义接口IDog、ICat实现IAnimal,定义类Dog、Cat分别实现以上接口,这种方式细化了接口,且给每个接口定义其专门的方法。
public class Client {
public static void main(String[] args) {
IDog dog = new Dog();
ICat cat = new Cat();
dog.bark();
dog.eat();
System.out.println("--------------------");
cat.meow();
cat.eat();
}
}
interface IAnimal{
void eat();
}
interface IDog extends IAnimal{
void bark();
}
interface ICat extends IAnimal{
void meow();
}
class Dog implements IDog{
public void eat() {
System.out.println("小狗正在吃骨头");
}
public void bark() {
System.out.println("小狗正在汪汪叫");
}
}
class Cat implements ICat{
public void eat() {
System.out.println("小猫正在吃鱼");
}
public void meow() {
System.out.println("小猫正在喵喵叫");
}
}
降低类对接口的依赖,减少程序的代码冗余
能够体现对象的层次,通过接口的继承,实现对总接口的定义
迪米特原则也称之最少知道原则,是一个面向对象设计原则,主张一个对象应该对其他对象有尽可能少的了解,只与直接的朋友通信。通俗的来说,一个类应该只关心与自己有直接关系的类,而不应该关心其他类的内部细节。当一个对象不需要直接与其他对象进行通信时,就不应该引用其他对象,而应该通过中间对象来进行简介通信。
避免与不相关的对象通信
定义一个通知类,用于作为通知内容,定义老师类作为第三方传达校领导的通知内容,定义学生类作为接收者,这样就可以由老师将通知传达给学生,避免不相关的对象进行通信。
public class Person {
public static void main(String[] args) {
Student student = new Student();
student.noticeStudent(new Teacher());
}
}
class Notices{
public void deliverNotices(){
System.out.println("明天举行运动会");
}
}
class Teacher{
private Notices notices =new Notices();
public Notices getNotices(){
System.out.print("校领导说:");
notices.deliverNotices();
return notices;
}
}
class Student{
public void noticeStudent(Teacher teacher){
System.out.println("老师说:");
teacher.getNotices();
}
}
降低类之间的耦合度,从而增强程序的可维护性和可扩展性
合成原则是指在面向对象的设计中,尽量使用对象组合,而不是通过继承来达到复用的目的。通俗的来说,通过使用已有对象来构建新的对象,可以在运行时动态地向这些对象的委派达到复用已有功能。
在面向对象的设计中,如果直接继承基类,会破坏封装,因为继承将基类的实现细节暴露给子类;如果基类的实现发生了改变,则子类的实现也不得不改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性。于是就提出了组合/聚合复用原则,也就是在实际开发设计中,尽量使用组合/聚合,不要使用类继承
通过已有对象构建新对象并复用已有功能
定义了项目(Project)、员工(Employee)、部门(Department)和公司(Company)这几个类。在公司类中,我们使用了聚合关系,即公司包含部门;而在部门类中,我们使用了聚合关系,即部门包含员工和项目。
public class Client {
public static void main(String[] args) {
Employee emp1 = new Employee("张三");
Employee emp2 = new Employee("李四");
Project proj1 = new Project("项目 A");
Project proj2 = new Project("项目 B");
Department dept1 = new Department("后端开发部门");
dept1.addEmployee(emp1);
dept1.addEmployee(emp2);
dept1.addProject(proj1);
dept1.addProject(proj2);
Company company = new Company();
company.addDepartment(dept1);
company.listDepartments();
}
}
// 项目类
class Project {
private String projectName;
public Project(String projectName) {
this.projectName = projectName;
}
public String getProjectName() {
return projectName;
}
}
// 员工类
class Employee {
private String name;
public Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// 部门类
class Department {
private String departmentName;
private List employees; // 聚合关系
private List projects; // 组合关系
public Department(String departmentName) {
this.departmentName = departmentName;
this.employees = new ArrayList();
this.projects = new ArrayList();
}
public void addEmployee(Employee employee) {
employees.add(employee);
}
public void addProject(Project project) {
projects.add(project);
}
public void listEmployees() {
System.out.println(departmentName +"中的员工 " + ":");
for (Employee employee : employees) {
System.out.println(employee.getName());
}
}
public void listProjects() {
System.out.println(departmentName +"管理的项目 " + ":");
for (Project project : projects) {
System.out.println(project.getProjectName());
}
}
}
// 公司类
class Company {
private List departments; // 聚合关系
public Company() {
this.departments = new ArrayList();
}
public void addDepartment(Department department) {
departments.add(department);
}
public void listDepartments() {
System.out.println("公司中的项目:");
for (Department department : departments) {
department.listEmployees();
department.listProjects();
}
}
}
使得类的修改对其他类的影响降到最低,提高了代码的灵活性和可维护性
提高类的内聚性,促进代码复用
单一职责原则是一个类不应该承担太多不同的职责,会增加类的复杂性,降低代码的可维护性。通俗的来说就是一个类只做一件事情,并且这件事情的变化是这个类变化的唯一原因
一个类只负责一项职责
海洋生物和陆地生物的呼吸方式不同,所以根据它们的呼吸方式不同,采用单一职责原则,这里就可以单独定义两个类对象,完成呼吸功能。
public class Client {
public static void main(String[] args) {
Terrestrial terrestrial = new Terrestrial();
terrestrial.breath("小狗");
Marine marine = new Marine();
marine.breath("小鱼");
}
}
//根据单一职责原则,不同的生物呼吸方式的不同,可以分为陆地生物和海洋生物
/**
* 陆地生物就呼吸空气
*/
class Terrestrial{
public void breath(String animal){
System.out.println(animal+"呼吸空气");
}
}
/**
* 海底生物就呼吸水
*/
class Marine{
public void breath(String animal){
System.out.println(animal+"呼吸水");
}
}
可以降低类的复杂度,提高代码的可维护性和可读性