设计模式设计原则

设计原则

前言:了解设计模式之前,一定要先理解什么设计原则,只有这样才能悟透设计模式的根本。

快速入口

  1. 工厂模式
  2. 单例模式

1.何为设计?

  • 按哪一种思路或者标准来实现的功能;
  • 功能相同,可以有不同设计的方式;
  • 需求如果不断变化,设计的作用才能体现出来;

2. SOLID 五大设计原则

  1. 单一职责原则(S) 单一功能原则认为对象应该仅具有一种 单一功能 的概念;
  2. 开放封闭原则(O)开闭原则认为 软件体应该是对于扩展开放的,但是对于修改封闭的 概念;
  3. 里式替换原则 (L)里式替换原则认为 程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的 概念;
  4. 接口隔离原则 (I) 接口隔离原则认为 多个特定客户端接口要好于一个宽泛用途的接口 的概念;
  5. 依赖反转原则 (D)依赖反转原则认为 应该遵从依赖于抽象而不是一个实例 的概念 ,依赖注入是该原则的一种实现方式;

2.1 单一职责原则 (Single Responsibility Principle)

  • 一个类或者模块只负责一个职责,如果功能特别复杂就进行拆分;
  • 单一职责可以降低类的复杂性,提高代码可读性、可维护性;
  • 当类代码函数过多、方法过多、功能太多、职责太杂的时候就要对类进行拆分了;
  • 拆分不能过度,如果拆分过度会损失内聚性和维护性;

一个商品类,有name,price属性,以及可以修改商品名,和修改价格,那么就可细分

/*** 商品类*/
class Commodity {public name: string;public price: number;// 更新商品名public updateName() { }// 更新商品价格public updatePrice() { }
}

一个类中的方法尽量维持在10个方法以内,这样可以做到高内聚和维护性,但是也不能过分拆分,取决于业务的功能。

2.2 开放封闭原则 (Open Closed Principle)

  • 对扩展开放,对修改关闭

  • 增加需求时,扩展新代码,而非修改已有代码;

  • 开闭原则是设计模式中的总原则;同时也是软件设计的终极目标;

  • 对近期可能会变化并且如果有变化但改动量巨大的地方要增加扩展点,扩展点过多会降低可读性;

例如顾客去商场购物,普通用户不打折,VIP用户打9折,那么我们现在用代码来实现一下


/*** 会员类* @param {string} rank 会员等级*/
class User {constructor(public rank: string) {this.rank = rank;}
}/*** 超市类* @param name 商品* @param price 价格*/
class Shopping {constructor(public name: string, public price: number) {}cashier(user: User) {switch (user.rank) {case 'general':console.log(`您是普通会员,折扣后的价格是:${this.price * 1}`);break;case 'vip':console.log(`您是vip会员,折扣后的价格是:${this.price * .9}`);break;default:console.log(`您的价格是:${this.price}`);break;}}
}let goods = new Shopping('MacBook Pro', 10000);
let general = new User('general');
let vip = new User('vip');
console.log(goods.cashier(general)); // 您是普通会员,给您的折扣是:10000
console.log(goods.cashier(vip)); // 您是普通会员,给您的折扣是:9000

假如现在超市又新增了一个黄金VIP,那么现在是不是得修改 Shopping 类中的 cashier 方法,这样就违背了 开放封闭原则 ,所以上面就不是一个完美的设计。

我们现在改进一下上面的代码, 让它符合 开放封闭原则


/*** 会员类* @param {string} rank 会员等级*/
class User {constructor(public rank: string, public discount: number = 1) {this.rank = rank;this.discount = discount;}
}/*** 超市类* @param name 商品* @param price 价格*/
class Shopping {constructor(public name: string, public price: number) { }cashier(user: User) {return this.price * user.discount;}
}let goods = new Shopping('MacBook Pro', 10000);
let general = new User('general');
let vip = new User('vip', .9);
let goldVip = new User('goldVip', .8);
console.log('折扣后的价格是:' + goods.cashier(general)); // 10000
console.log('折扣后的价格是:' + goods.cashier(vip)); // 9000
console.log('折扣后的价格是:' + goods.cashier(goldVip)); // 8000

前端实际应用中符合该原则的较多,几乎大部分库都符合该原则,例如 Axios 中的拦截器;

2.3 里式替换原则 (Liskov Substitution Principle)

  • 所有引用基类的地方必须能透明地使用其子类的对象;
  • 子类能替换掉父类,使用者可能根本就不需要知道是父类还是子类,反之则不行;
  • 里式替换原则是开闭原则的实现基础,程序设计的时候尽量使用基类定义及引用,运行时再决定使用哪个子类;
  • 里式替换原则可以提高代码的复用性,提高代码的可扩展性,也增加了耦合性;
  • 相对于多态,这个原则是讲的是类如何设计,子类如果违反了父类的功能则表示违反了里式替换原则

一个用去买咖啡喝,那么想要知道当前购买咖啡的价格

	/*** 尽可能使用父类或者抽象类* 任何再能使用父类的地方都要可以使用子类* 类似多态*/abstract class AbstractCoffe {abstract getPrice(): number;
}class Mocha extends AbstractCoffe {getPrice() {return 28;}
}class Americano extends AbstractCoffe {getPrice() {return 26;}
}class Cappuccino extends AbstractCoffe {getPrice() {return 30;}
}class Customer {// 这里可以传父类,也可以传子类,所以就可以替换// 这就是里式替换原则// 但是这里的子类不能违反父类的约定,也就是 子类中的 getPrice 不能反悔其它的类型,只能返回 Numberdrink(abstractCoffe: AbstractCoffe) {console.log("这个咖啡价格是:" + abstractCoffe.getPrice());}
}let c1 = new Customer();
c1.drink(new Mocha())
c1.drink(new Americano())
c1.drink(new Cappuccino())

2.4 依赖倒置原则(Dependence Inversion Principle)

  • 面向接口编程,依赖于抽象而不依赖于具体实现;
  • 要求我们在程序代码中传递参数时或在光联关系中,尽量引用层次高的抽象层类;
  • 使用方只关注接口而不关注具体类的实现;
/*** 依赖抽象,而非依赖具体的实现*/interface GrilFriend {age: number;height: number;weight: number;beautiful(): Boolean
}class TongLiYa implements GrilFriend {age: 25;height: 168;weight: 105;beautiful() {return true}
}class XiaoHone implements GrilFriend {age: 25;height: 168;weight: 105;beautiful() {return true}
}class SingleDog {constructor(public grilFriend: GrilFriend) { }
}let dog1 = new SingleDog(new TongLiYa());// 不需要是TongLiYa ,但是可以与她类似就行了,依赖抽象GrilFriend,而非具体的 TongLiYa
let dog2 = new SingleDog(new XiaoHone());

2.5 接口隔离原则(Interface Segregation Principle)

  • 保持接口的单一独立,避免出现胖接口;
  • 客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上;
  • 接口尽量细化,而且接口中的方法尽量的少;
  • 类似于单一职责原则,更关注接口;
interface IUerManager {updateUserInfo(): void; // 更新用户信息updatePassword(): void; // 更新密码
}interface IProductManager {updateProduct(): void;updatePrice(): void;
}/************************* 举例说明 ****************************/
/*** 可以吃饭的接口*/
interface IEating {eat(): void
}/*** 可以吃唱歌的接口*/
interface ISinging {singing(): void
}/*** 可以说话的接口*/
interface ISpeaking {speaking(): void
}// 我可以唱歌,可以吃饭,会讲话
// 拆分成三个,1. 为了复用,2. 为了低耦合,3. 为了单一职责
class Me implements IEating, ISinging, ISpeaking {eat() { }singing() { }speaking() { }
}// zhangsan 只会吃
class Zhangsan implements IEating {eat() { }
}

3. 迪米特法则 (Law of Demter, LOD)

  • 有时候也叫做最少知识原则;
  • 一个软件实体应当尽可能少地与其它实体发生相互作用;
  • 迪米特法则的初衷在于降低类之间的耦合;
  • 类定义时尽量要实现内聚,少使用 public 修饰符,尽量使用 private、protected 等;
  • PS:现实中得你自己的对象,你只需要了解自己的对象的习惯爱好,你对别人的对象尽可能少的了解;

老板需要知道公司产品的一个名字,那么他就问了经理,经理就问了员工A


/*** 员工A*/
class StaffA {getProductName() {console.log("这个产品的名字是:XXX");}
}/*** 经理*/
class Manager {private staffA: StaffA = new StaffA();getProductName() {this.staffA.getProductName()}
}/*** Boss*/class Boss {private manager: Manager = new Manager();getProductName() {this.manager.getProductName()}
}let boss = new Boss();
boss.getProductName();

4. 合成复用原则

2.1 类的关系

  • 类之间有三种基本关系,分别是关联(聚合和组合)、泛化和依赖
  • 如果一个类单向依赖另一个类,那么它们之间就是 单向关联。如果彼此依赖,则为相互依赖,即 双向关联
  • 关联关系包括两种特例:聚合和组合
    • 聚合,用来表示整体与部分的关系或者拥有关系,代表部分的对象可能会被整体拥有,但并不一定会随着整体的消亡而销毁,比如班级和学生
    • 合成或者说组合要比聚合关系强的多,部分和整体的生命周期是一致的,比如人和器官之间;
// 一个班级里面会有多个学生,还有老师,这就是聚合的关系
class Class {public students: Array<Student>public teacher: Teacher = new Teacher()
}// 学生和老师都有书,这是组合的关系
class Student {public book: Book;
}// 学生和老师都有书,这是组合的关系
class Teacher {public book: Book;
}class Book { }// 学生和老师就是人的泛化,泛化关系
class Person { }// 老师教书依赖黑板,依赖关系
class Block { }

2.2 合成复用原则

  • 合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的;
  • 新对象可以调用已有对象的功能,从而达到复用;
  • 原则是尽量首先使用组合/聚合的方式,而不是使用继承;
  • 也就是说,专业的人做专业的事;
// 尽量使用组合或者聚合原则,而不是使用继承,因为继承的耦合性太强了
class Cooker {cook() { }
}// class Person2 extends Cooker{ 尽量不使用继承
class Person2 {private cooker: Cooker;cook() {this.cooker.cook();}
}

5. 总结

  • 开闭原则是核心,对修改关闭对扩展开放是软件设计的基石;
  • 单一职责要求我们设计接口和模块功能的时候尽量保证单一性,修改一条不影响全局和其它模块;
  • 里式替换原则和依赖倒置原则要求面向接口和抽象编程,不要依赖具体实现,否则实现一改,上层调用者就要对应修改;

6. 如何写出好的代码?

  • 可维护性 BUG是否好改?
  • 可读性 是否容易看懂?
  • 可扩展性 是否可以添加新功能?
  • 灵活性 添加新功能是否容易?老方法和接口是否容易复用?
  • 简洁性 代码是否简单清晰?
  • 可复用性 相同的代码不要写多遍?
  • 可测试性 是否方便写单元测试和集成测试?

7. 23 种设计模式

创建性(5):

  • 工厂模式(简单工厂模式 详解1、工厂方法模式、抽象工厂模式)、建造者模式、单例模式
  • 原型模式

结构性模式(7):

  • 代理模式、桥接模式、装饰器模式、适配器模式
  • 外观模式、组合模式、享元模式

行为型(11):

  • 观察者模式、模板方法模式、策略模式、职责链模式、迭代器模式、状态模式
  • 访问者模式、备忘录模式、命令模式、解释器模式、中介者模式

详解1: 简单工厂不在23种设计模式中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/7055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马苍穹外卖

sky-pojo子模块内容 Entity&#xff1a;实体&#xff0c;通常和数据库中的表对应DTO&#xff1a;数据传输对象&#xff0c;通常用于程序中各层之间传递数据&#xff08;eg&#xff1a;前端返回的json数据&#xff0c;后端要接收&#xff0c;并且转化为java对象&#xff0c;此时…

QtConcurrent::run操作界面ui的注意事项(1)

先说结论&#xff1a;QtConcurrent::run启动的耗时处理函数&#xff0c;不允许处理ui界面对象&#xff0c;如控件&#xff0c;如进度条等等&#xff01; QtConcurrent::run非常好用&#xff0c;胜过QThead的两种方式&#xff08;run和moveToThread&#xff09;&#xff0c;例如…

拆炸弹(Lc1652)——模拟

你有一个炸弹需要拆除&#xff0c;时间紧迫&#xff01;你的情报员会给你一个长度为 n 的 循环 数组 code 以及一个密钥 k 。 为了获得正确的密码&#xff0c;你需要替换掉每一个数字。所有数字会 同时 被替换。 如果 k > 0 &#xff0c;将第 i 个数字用 接下来 k 个数字之…

ERP系统电子文件归档和电子档案管理规范

ERP系统电子文件归档和电子档案管理规范 1 范围 本文件描述了企业资源计划&#xff08;ERP&#xff09;系统形成电子文件归档和电子档案管理的方法。 本文件适用于企业资源计划&#xff08;ERP&#xff09;系统&#xff08;含采购、销售、物料、生产计划、质量、设备、项目…

使用C#和EF Core实现高效的SQL批量插入

在软件开发中&#xff0c;批量插入数据是一个常见的需求&#xff0c;特别是在数据迁移、初始化数据库或进行大量数据处理时。Entity Framework Core (EF Core) 是一个流行的.NET对象关系映射器&#xff08;ORM&#xff09;&#xff0c;它简化了数据库操作&#xff0c;但在进行大…

Linux CPU 飙升 排查五步法

排查思路-五步法 1. top命令定位应用进程pid 找到最耗时的CPU的进程pid top2. top-Hp[pid]定位应用进程对应的线程tid 找到最消耗CPU的线程ID // 执行 top -Hp [pid] 定位应用进程对应的线程 tid // 按shift p 组合键&#xff0c;按照CPU占用率排序 > top -Hp 111683.…

用wordpress建跨境电商独立站的5大优势

免费和开源 WordPress是一个免费的开源内容管理系统&#xff0c;用户可以自由下载、安装和使用&#xff0c;无需支付版权费用或订阅费用。开源特性也意味着用户可以根据自己的需求修改和定制代码&#xff0c;或者使用其他开发者提供的插件和主题来扩展和美化网站。 易用和灵活…

探索Vue 3.0中的v-html指令

探索Vue 3.0中的v-html指令 一、什么是v-html指令&#xff1f;1、 在Vue 3.0中使用v-html2、 注意事项 二、结语 一、什么是v-html指令&#xff1f; Vue.js作为一款流行的JavaScript框架&#xff0c;不断地演进着。随着Vue 3.0的发布&#xff0c;开发者们迎来了更加强大和灵活…

CMS清理流程及问题

CMS&#xff08;Concurrent Mark Sweep&#xff0c;并发标记清除&#xff09;收集器是以获取最短回收停顿时间为目标的收集器&#xff08;追求低停顿)&#xff0c;它在垃圾收集时使得用户线程和GC线程并发执行&#xff0c;因此在垃圾收集过程中用户也不会感到明显的卡顿。从名字…

SparkSQL编程入口和模型与SparkSQL基本编程

SparkSQL编程入口和模型 SparkSQL编程模型 主要通过两种方式操作SparkSQL&#xff0c;一种就是SQL&#xff0c;另一种为DataFrame和Dataset。 1)SQL&#xff1a;SQL不用多说&#xff0c;就和Hive操作一样&#xff0c;但是需要清楚一点的是&#xff0c;SQL操作的是表&#xf…

为什么很多人不推荐你用JWT?

为什么很多人不推荐你用JWT? 如果你经常看一些网上的带你做项目的教程&#xff0c;你就会发现 有很多的项目都用到了JWT。那么他到底安全吗&#xff1f;为什么那么多人不推荐你去使用。这个文章将会从全方面的带你了解JWT 以及他的优缺点。 什么是JWT? 这个是他的官网JSON…

Linux学习(一)-- 简单的认识

目录 1. Linux的诞生 2.Linux发行版 拓展&#xff1a; &#xff08;1&#xff09;什么是Linux系统的内核&#xff1f; &#xff08;2&#xff09;什么是Linux系统发行版&#xff1f; 1. Linux的诞生 Linux创始人: 林纳斯 托瓦兹 Linux 诞生于1991年&#xff0c;作者上大学…

2×24.5W、内置 DSP、低失真、高信噪比、I2S 输入 D 类音频功率放大器,完美替换TPA5805,晶豪,致盛,

ANT3825 是一款高集成度、高效率的双通道数字 输入功放。供电电压范围在 5V&#xff5e;18V&#xff0c;数字接口 电源支持 3.3V 或 1.8V。双通道 BTL 模式下输出 功率可以到 224.5W(4Ω&#xff0c;16V&#xff0c;THDN1%)&#xff0c; 单通道 PBTL 模式下可以输出 37W&#x…

Spring IoCDI(2)—IoC详解

目录 一、IoC详解 1、Bean的存储 &#xff08;1&#xff09;Controller&#xff08;控制器存储&#xff09; 获取bean对象的其他方式 Bean 命名约定 &#xff08;2&#xff09;Service&#xff08;服务存储&#xff09; &#xff08;3&#xff09;Repository&#xff08…

注册英国公司都有什么好处

注册英国公司是一项相对简单直接的程序&#xff0c;通常需要通过 Companies House&#xff08;英国公司注册局&#xff09;完成。以下是注册英国公司的步骤&#xff1a; 选择公司名称&#xff1a; 首先&#xff0c;您需要选择一个独特的公司名称。您可以通过Companies House的…

2分钟教你Flutter怎么避免引用内存泄漏

2分钟教你Flutter怎么避免引用内存泄漏 内存泄漏原因1. 在当前类&#xff0c;或者方法等移除改引用&#xff0c;让其他自动释放&#xff0c;等下一轮GC扫描释放。如2. 使用弱引用-----**WeakReference**&#xff0c;当前使用完&#xff0c;生命周期结束后&#xff0c;自动释放。…

VTK —— 三、图形格式 - 示例1 - 读取.vtp文件并输出.ply文件(附完整源码)

代码效果&#xff1a;演示程序读取.vtp后输出.ply文件&#xff0c;使用paraview打开该输出的.ply文件 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用V…

opencv图像处理详细讲

传统的计算机视觉框架&#xff1a; SimpleCV BoofCV Dlib JavaCV 深度学习计算机视觉框架 Caffe Tensorflow Pytorch Paddlepaddle Keras 深度视觉计算机视觉框架 OpenVINO TensorRT onnxruntime Deepface YOLO/DarkNet mmdetection Paddle-detection/seg/ocr …

重识来伊份:抢滩首店经济,休闲零食品牌的“面子”和“里子”

前不久&#xff0c;苹果静安零售店的首秀频频登上热搜。 这背后&#xff0c;不仅仅因为它是中国大陆最大的苹果旗舰店&#xff0c;还在于它的设计融入了时尚又古典的上海街区&#xff0c;吸引了众多市民拍照打卡。今年3月至5月&#xff0c;上海会持续举办“首发上海”春季系列…

电能计量抄表系统

1.电能计量抄表系统的概念和作用 电能计量抄表系统是一种前沿的自动化控制&#xff0c;主要运用于电力行业的电费计算及管理。它通过远程数据采集和处理&#xff0c;实时监控系统用户的用电状况&#xff0c;取代了传统的手工抄水表方法&#xff0c;降低了成本&#xff0c;降低…