设计模式——七大原则(附代码示例)
一. 设计模式概念
对接口编程而不是对实现编程;优先使用对象组合而不是继承
二. 设计模式总览
1. 创建型模式(Creational Patterns):(5)
单例(Singleton)模式 原型(Prototype)模式 工厂方法(FactoryMethod)模式
抽象工厂(AbstractFactory)模式 建造者(Builder)模式
2. 结构型模式(Structural Patterns): (7)
代理(Proxy)模式 适配器(Adapter)模式 桥接(Bridge)模式
装饰(Decorator)模式 外观(Facade)模式 享元(Flyweight)模式
组合(Composite)模式 过滤器模式(Filter Pattern)
3. 行为型模式(Behavioral Patterns): (11)
模板方法(Template Method)模式 策略(Strategy)模式 命令(Command)模式
职责链(Chain of Responsibility)模式 状态(State)模式 观察者(Observer)模式
中介者(Mediator)模式 迭代器(Iterator)模式 访问者(Visitor)模式
备忘录(Memento)模式 解释器(Interpreter)模式
4. 划分依据
在编写代码时,其实就是对组件(类、接口等)的一系列操作。所以设计模式可以根据组件的生命周期来划分,在组件定义环节如何构建组件就会用到结构型模型;在组件创建环节使用创建型模式;在组件的使用环节可用行为型模式。当然,每个环节并不是独立的,所以各个环节所应用的模式都应该互相考量。
三. 设计模式七大原则
设计模式常用的七大原则有:
1)单一职责原则 2)接口隔离原则 3)依赖倒转(倒置)原则 4)里氏替换原则
5)开闭原则 6)迪米特法则 7)合成复用原则
1. 单一职责原则
一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。即每个类只负责自己的事情,而不是变成万能
代码示例
以交通工具的使用为例:
方案1,违法单一职责原则
public class SingleResponsibility1 {public static void main(String[] args) {Vehicle vehicle = new Vehicle();vehicle.run("汽车");vehicle.run("摩托");vehicle.run("飞机");}
}
//方案1,违法单一职责原则
class Vehicle{public void run(String vehicle){System.out.println(vehicle+"在公路上行驶");}
}
方案2,遵守单一职责原则,但是改动很大,要将类分解,且修改客户端
public class SingleResponsibility2 {public static void main(String[] args) {RoadVehicle roadVehice = new RoadVehicle();roadVehice.run("汽车");roadVehice.run("摩托");AirVehicle airVehicle = new AirVehicle();airVehicle.run("飞机");}
}
//方案2,遵守单一职责原则,但是改动很大,要将类分解,且修改客户端
class RoadVehicle{public void run(String vehicle){System.out.println(vehicle+"在公路上行驶");}
}
class AirVehicle{public void run(String vehicle){System.out.println(vehicle+"在天空上行驶");}
}
方案3,没有对原来类对大的修改,只是增加方法,虽然没有在类这个级别上遵守单一职责原则,但在方法级别上仍然遵守单一职责原则
public class SingleResponsibility3 {public static void main(String[] args) {Vehicle2 vehicle = new Vehicle2();vehicle.run("汽车");vehicle.run("摩托");vehicle.runAir("飞机");}
}
//方案3,没有对原来类对大的修改,只是增加方法
// 虽然没有在类这个级别上遵守单一职责原则,但在方法级别上仍然遵守单一职责原则
class Vehicle2{public void run(String vehicle){System.out.println(vehicle+"在公路上行驶");}public void runAir(String vehicle){System.out.println(vehicle+"在天空上行驶");}
}
2. 接口隔离原则
一个类对另一个类的依赖应该建立在最小的接口上。即各个类建立自己的专用接口,而不是建立万能接口
代码示例
实现下列UML: 类B实现五个方法;类D实现五个方法;类A通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法;类C通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法
不足:类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。
public class Segregation1 {public static void main(String[] args) {Interface1 B = new B();A a = new A();a.depend1(B);a.depend2(B);a.depend3(B);}
}
//接口定义五个方法
interface Interface1{void operation1();void operation2();void operation3();void operation4();void operation5();
}
//类B实现五个方法
class B implements Interface1{@Overridepublic void operation1() {System.out.println("B实现operation1");}@Overridepublic void operation2() {System.out.println("B实现operation2");}@Overridepublic void operation3() {System.out.println("B实现operation3");}@Overridepublic void operation4() {System.out.println("B实现operation4");}@Overridepublic void operation5() {System.out.println("B实现operation5");}
}
//类D实现五个接口
class D implements Interface1{@Overridepublic void operation1() {}@Overridepublic void operation2() {}@Overridepublic void operation3() {}@Overridepublic void operation4() {}@Overridepublic void operation5() {}
}
//类A通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法
class A{public void depend1(Interface1 i){i.operation1();}public void depend2(Interface1 i){i.operation2();}public void depend3(Interface1 i){i.operation3();}
}
//类C通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法
class C{public void depend1(Interface1 i){i.operation1();}public void depend4(Interface1 i){i.operation4();}public void depend5(Interface1 i){i.operation5();}
}
改进:使用接口隔离原则
public class SegregationImprove {public static void main(String[] args) {A a = new A();a.depend1(new B());a.depend2(new B());a.depend3(new B());}
}//接口1
interface Interface1{void operation1();
}
//接口2
interface Interface2{void operation2();void operation3();
}
//接口3
interface Interface3{void operation4();void operation5();
}//类B实现
class B implements Interface1,Interface2{@Overridepublic void operation1() {System.out.println("B实现operation1");}@Overridepublic void operation2() {System.out.println("B实现operation2");}@Overridepublic void operation3() {System.out.println("B实现operation3");}
}//类D实现
class D implements Interface1,Interface3{@Overridepublic void operation1() {}@Overridepublic void operation4() {}@Overridepublic void operation5() {}
}class A{public void depend1(Interface1 i){i.operation1();}public void depend2(Interface2 i){i.operation2();}public void depend3(Interface2 i){i.operation3();}
}class C{public void depend1(Interface1 i){i.operation1();}public void depend4(Interface3 i){i.operation4();}public void depend5(Interface3 i){i.operation5();}
}
3. 依赖倒转(倒置)原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。即面向接口编程,而不是面向实现类
代码示例
完成Person接受消息功能:方式1,简单比较容易想到,但是如果我们获取的对象是微信等,就要新增类和相应的方法
public class DependenceReversal1 {public static void main(String[] args) {Person person = new Person();person.receive(new Email());person.receive02(new Wechat());}
}
class Email{public String getInfo(){return "电子邮件信息:hello";}
}
//增加微信
class Wechat{public String getInfo(){return "微信信息:hello";}
}
//方式1,简单比较容易想到,但是如果我们获取的对象是微信等,就要新增类和相应的方法
class Person{public void receive(Email email){System.out.println(email.getInfo());}public void receive02(Wechat wechat){System.out.println(wechat.getInfo());}
}
改进:引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖。因为Email,微信等都属于接收者的范围,各自实现IReceiver,符合依赖倒转原则,无需新增方法
public class DependenceReversal {public static void main(String[] args) {//客户端无需改变Person person = new Person();person.receive(new Email());//新增微信person.receive(new Wechat());}
}
//定义接口
interface IReceiver{public String getInfo();
}
class Email implements IReceiver{public String getInfo(){return "电子邮件信息:hello";}
}
//增加微信
class Wechat implements IReceiver{public String getInfo(){return "微信信息:hello";}
}
//方式2,对接口的依赖
class Person{public void receive(IReceiver receiver){System.out.println(receiver.getInfo());}
}
依赖的3种实现传递的方式
方式1:通过接口传递实现依赖
public class DependenceReversal {public static void main(String[] args) {ChangHong changHong = new ChangHong();OpenAndClose openAndClose = new OpenAndClose();openAndClose.open(changHong);}
}
//方式1:通过接口传递实现依赖
// 开关接口
interface IOpenAndClose{public void open(ITV itv);
}
//ITV接口
interface ITV{public void play();
}
//长虹电视实现
class ChangHong implements ITV{@Overridepublic void play() {System.out.println("打开长虹电视机");}
}
//实现接口
class OpenAndClose implements IOpenAndClose{@Overridepublic void open(ITV itv) {itv.play();}
}
方式2:通过构造方法依赖传递
public class DependenceReversal {public static void main(String[] args) {ChangHong2 changHong2 = new ChangHong2();OpenAndClose2 close2 = new OpenAndClose2(changHong2);close2.open();}
}
//方式2:通过构造方法依赖传递
interface IOpenAndClose2{public void open();
}
interface ITV2{public void play();
}
//长虹电视实现
class ChangHong2 implements ITV2{@Overridepublic void play() {System.out.println("打开长虹电视机");}
}
class OpenAndClose2 implements IOpenAndClose2{public ITV2 itv;public OpenAndClose2(ITV2 itv){this.itv = itv;}@Overridepublic void open() {this.itv.play();}
}
方式3:通过setter方法传递
public class DependenceReversal {public static void main(String[] args) { ChangHong3 changHong3 = new ChangHong3();OpenAndClose3 close3 = new OpenAndClose3();close3.setTv(changHong3);close3.open();}
}
//方式3:通过setter方法传递
interface IOpenAndClose3{public void open();public void setTv(ITV3 tv);
}
interface ITV3{public void play();
}
//长虹电视实现
class ChangHong3 implements ITV3{@Overridepublic void play() {System.out.println("打开长虹电视机");}
}
class OpenAndClose3 implements IOpenAndClose3{public ITV3 itv;@Overridepublic void open() {this.itv.play();}@Overridepublic void setTv(ITV3 tv) {this.itv = tv;}
}
4. 里氏替换原则
继承必须确保超类所拥有的性质在子类中仍然成立。即继承父类而不去改变父类
代码示例
B类继承A类,增加一个新功能:两数相加,但是误将A类方法1重写,导致错误
//A类
class A{//返回两数之差public int func1(int num1,int num2){return num1-num2;}
}
//B类继承A类,增加一个新功能:两数相加,但是误将A类方法1重写,导致错误
class B extends A{public int func1(int a,int b){return a+b;}public int func2(int a,int b){return func1(a,b)+9;}
}
改进:创建一个更加基础的基类,把更加基础的方法和成员写在基类中,B类,如果需要使用A类方法,可采用组合方式
//创建一个更加基础的基类
class Base{//把更加基础的方法和成员写在基类中
}
//A类
class A extends Base{//返回两数之差public int func1(int num1,int num2){return num1-num2;}
}
//B类,如果需要使用A类方法,可采用组合方式
class B extends Base{private A al = new A();public int func2(int a,int b){return al.func1(a,b)+9;}
}
5. 开闭原则
软件实体应当对扩展开放,对修改关闭。即扩展新类而不是修改旧类
(合成复用原则、里氏替换原则相辅相成,都是开闭原则的具体实现规范)
代码示例
以绘图为例,根据m_type不同调用不同方法绘制不同图形,优点是比较好理解,简单易操作。缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。比如我们这时要新增加一个图形种类,我们需要修改很多地方(创建新类,增加方法,增加判断)。
public class OpenAndClose {public static void main(String[] args) {GraphicEditor editor = new GraphicEditor();editor.drawShape(new Circle());editor.drawShape(new Rectangle());}
}
class GraphicEditor{public void drawShape(Shape s){if(s.m_type == 1){drawRectangle(s);}else {drawCircle(s);}}public void drawRectangle(Shape r){System.out.println("矩形");}public void drawCircle(Shape r){System.out.println("圆形");}
}
class Shape{int m_type;
}
class Rectangle extends Shape{Rectangle(){super.m_type = 1;}
}
class Circle extends Shape{Circle(){super.m_type = 2;}
}
改进:把Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可,使用方的代码就不需要修改,满足了开闭原则。
public class OpenAndClose {public static void main(String[] args) {GraphicEditor editor = new GraphicEditor();editor.drawShape(new Circle());editor.drawShape(new Rectangle());}
}
class GraphicEditor{public void drawShape(Shape s){s.draw();}
}
abstract class Shape{public abstract void draw();
}
class Rectangle extends Shape{@Overridepublic void draw() {System.out.println("绘制矩形");}
}
class Circle extends Shape{@Overridepublic void draw() {System.out.println("绘制圆形");}
}
6. 迪米特法则
最少知识原则,只与你的直接朋友交谈,不跟“陌生人”说话。即无需直接交互的两个类,如果需要交互,使用中间者
直接朋友: 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部
代码示例
以学院和学校为例,打印出所有员工。分析SchoolManager直接朋友是Employee,CollegeManager,但是CollegeEmployee不是直接朋友而是以局部变量的方式出现在类内部,违法了迪米特法则
public class Dimilit {public static void main(String[] args) {SchoolManager schoolManager = new SchoolManager();schoolManager.printAllEmployee(new CollegeManager());}
}//学校总部员工类
class Employee{private String id;public void setId(String id){this.id = id;}public String getId(){return id;}
}
//学院员工类
class CollegeEmployee{private String id;public void setId(String id){this.id = id;}public String getId(){return id;}
}
//管理学院类
class CollegeManager{//返回学院员工public List<CollegeEmployee> getAllEmployee(){List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();for (int i = 0; i < 10; i++) {CollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id= "+i);list.add(emp);}return list;}
}
//管理学校类
class SchoolManager{//返回学校总部员工public List<Employee> getAllEmployee(){List<Employee> list = new ArrayList<Employee>();for (int i = 0; i < 5; i++) {Employee emp = new Employee();emp.setId("学院员工id= "+i);list.add(emp);}return list;}//打印所有员工void printAllEmployee(CollegeManager collegeManager){List<CollegeEmployee> list1 = collegeManager.getAllEmployee();System.out.println("------------学院员工-------------");for (CollegeEmployee collegeEmployee : list1) {System.out.println(collegeEmployee.getId());}List<Employee> list2 = this.getAllEmployee();System.out.println("------------总部员工-------------");for (Employee employee : list2) {System.out.println(employee.getId());}}
}
改进:将输出学院的员工方法,封装到CollegeManager
public class Dimilit {public static void main(String[] args) {SchoolManager schoolManager = new SchoolManager();schoolManager.printAllEmployee(new CollegeManager());}
}//学校总部员工类
class Employee{private String id;public void setId(String id){this.id = id;}public String getId(){return id;}
}//学院员工类
class CollegeEmployee{private String id;public void setId(String id){this.id = id;}public String getId(){return id;}
}//管理学院类
class CollegeManager{//返回学院员工public List<CollegeEmployee> getAllEmployee(){List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();for (int i = 0; i < 10; i++) {CollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id= "+i);list.add(emp);}return list;}//输出学院员工信息public void printEmployee(){List<CollegeEmployee> list1 = getAllEmployee();System.out.println("------------学院员工-------------");for (CollegeEmployee collegeEmployee : list1) {System.out.println(collegeEmployee.getId());}}
}//管理学校类
class SchoolManager{//返回学校总部员工public List<Employee> getAllEmployee(){List<Employee> list = new ArrayList<Employee>();for (int i = 0; i < 5; i++) {Employee emp = new Employee();emp.setId("学院员工id= "+i);list.add(emp);}return list;}//打印所有员工void printAllEmployee(CollegeManager collegeManager){//输出学院员工collegeManager.printEmployee();//输出学校员工List<Employee> list2 = this.getAllEmployee();System.out.println("------------总部员工-------------");for (Employee employee : list2) {System.out.println(employee.getId());}}
}
7. 合成复用原则
软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。即优先组合,其次继承
代码示例
Head类与Person类同生死,组合关系;IDCard类与Person,聚合关系
class Person {private IDCard card; //聚合关系private Head head = new Head(); //组合关系
}
class IDCard {}
class Head {}
四.设计模式核心思想
1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一 起
2. 针对接口编程,而不是针对实现编程
3. 为了交互对象之间的松耦合设计而努力
设计模式——七大原则(附代码示例)相关推荐
- C语言和设计模式大全(附代码示例)
文章目录 C语言和设计模式(之单件模式) C语言和设计模式(之原型模式) C语言和设计模式(之组合模式) C语言和设计模式(之模板模式) C语言和设计模式(工厂模式) C语言和设计模式(责任链模式) ...
- 设计模式——七大原则
设计模式--七大原则 汇总篇 1.单一职责 2.接口隔离 3.依赖倒转 4.里氏代换原则 5.开闭原则 6.迪米特法则 7.合成复用 汇总篇 1.单一职责 对类来说的,即一个类应该只负责一项职责.如类 ...
- 备战面试日记(3.1) - (设计模式.七大原则)
本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2022.1.4 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. 文章 ...
- Java面试之设计模式七大原则
最近项目不太忙,不怎么加班,正利用晚上时间好好学习学习设计模式,之前可能多多少少都用到过,但是有些还是很模糊,这下正好系统的学一下. 好了,话不多说,进入正题. 1.什么是设计模式? 软件工程中,设计 ...
- 第 2 章 设计模式七大原则
第 2 章 设计模式七大原则 1.设计模式的目的 编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战, 设计模式是为了让程序(软件),具有如下更好的 ...
- 图解设计模式-设计模式七大原则
Java设计模式 设计模式七大原则 设计模式的目的 编写软件过程中,程序员面临来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让 **程序(软件)**具有更好的 ...
- Day305.设计模式七大原则 -Java设计模式
七大设计原则 一.设计模式的目的 编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好的↓↓↓ 1. 代码重用 ...
- Java设计模式七大原则-单一职责原则
目录 概述:设计模式的目的 设计模式七大原则 单一职责原则 单一职责原则注意事项和细节 概述:设计模式的目的 编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等 ...
- 设计模式七大原则介绍
文章目录 1. 设计模式有哪些种类 2. 设计模式的目的 3. 设计模式七大原则 3.1. 单一职责原则 1. 基本介绍 2. 模拟场景 2. 接口隔离原则 1. 基本介绍 2. 模拟场景 3. 依赖 ...
最新文章
- WKWebView 实现iOS与H5的交互转
- Entity Framework快速入门笔记第四篇—ModelFirst
- shell中的条件表达式
- cpu高对计算机有什么影响吗,CPU损坏对电脑造成哪些影响
- WordPress 常用数据库SQL查询语句大全
- snort 使用mysql的安装
- Ubuntu系统下ntp服务器搭建
- eclipse中配置server
- CentOS 7 重装mysql编译过程报错解决方法
- 防火墙双机热备配置实例(二)
- 第四章 数字调制方式
- java字符常量_java字符常量
- 破解qq上网限制 突破限制上QQ
- vue 创建项目之vue init webpack xxx
- 数字信号处理 实验一 时域采样与频域采样【实验报告】
- mysql like 原理_MySql原理
- 禁止Win系统自动唤醒
- 计算机提示无法识别usb,小编教你电脑出现无法识别usb设备怎么办
- 睿爸信奥-【临阵磨枪】练习赛(第二场)- T2
- python django 安装虚拟环境创建工程项目