关于设计原则:面向对象的SOLID设计原则

在程序设计畛域,罗伯特·C·马丁指出了面向对象编程和面对对象设计的五大根本准则:SOLID。开发一个零碎时,如果利用这些准则,将会让零碎变得更加易于保护和扩大。 SOLID是由五个设计准则的首字母组成的: S - 繁多职责准则 - Single Responsibility PrincipleO - 凋谢关闭准则 - Open Close PrincipleL - 里氏替换准则 - Liskov Substitution PrincipleI - 接口拆散准则 - Interface Segregation PrincipleD - 依赖倒置准则 - Dependency Inversion Principle上面就SOLID五大设计准则,一起来看看。 S:繁多职责准则(Single Responsibility Principle)一个类(模块、函数等),只有一个职责;将多职责的类拆分为多个类,放弃各个类的互相独立。 益处:进步代码的可读性和可维护性,升高代码的复杂度,缩小批改代码的影响范畴。 任何代码都有可能批改。如果在一个类中实现多个不相干的职责,那么在批改其中一个职责的相干代码时,很有可能影响到其余职责的相干代码,造成这个类的不稳固影响范畴变大。 因而须要将多职责的类拆分为单职责的类,即便某一个职责相干代码须要批改,其影响范畴仅限于这个类,其余职责的代码不受这个职责代码批改的影响。 例如: // 一个光猫类,有两个职责:一个是治理连贯(dial和hangup);一个是数据传输(send和receive)class Model { dial (pno: string) {} hangup () {} send (msg: string) {} receive(data: string) {}}// 通常这两个职责并没有共同点,在一个类中实现,过于耦合。应该将其离开到两个绝对独立的类中,别离保护。class Connection { dial (pno: string) {} hangup () {}}class DataChannel { send (msg: string) {} receive(data: string) {}}class Model { constructor () { const connection = new Connection() const dataChannel = new DataChannel() }}O:凋谢关闭准则(Open Close Principle)当增加一个新的性能时,应该在已有代码的根底上扩大代码(新增模块、类、函数等),而不是批改已有的代码(批改模块、类、函数等)。 ...

May 10, 2023 · 2 min · jiezi

关于设计原则:代码设计原则

代码设计准则是用于进步代码品质、易读性和可维护性的规定和准则。它们的目标是使代码牢靠、易懂、易保护和易扩大。以下是一些罕用的代码设计准则: 1.繁多职责准则(Single Responsibility Principle):每个类或办法只负责一项工作,不应该有太多的职责。 2.开闭准则(Open/Closed Principle):对于扩大凋谢,对于批改敞开。类或模块应该能够扩大,但不应该批改已有的代码。 3.里氏替换准则(Liskov Substitution Principle):子类能够代替父类,并且不会导致任何谬误。 4.依赖倒置准则(Dependency Inversion Principle):高层模块不应该依赖低层模块,两者应该依赖其余形象层。 5.接口隔离准则(Interface Segregation Principle):客户端不应该依赖它不须要的接口。 6.合成复用准则(Composite Reuse Principle):通过合成复用来实现代码复用,而不是通过继承。 7.高内聚准则(High Cohesion Principle):每个类或模块应该有一个繁多的职责,且应该与其余局部相关联。 8.低耦合准则(Low Coupling Principle):各个模块之间的关系应该尽可能地低耦合,以缩小代码之间的相互影响。 9.可读性准则(Readability Principle):代码应该是易读的,通过正当的命名、缩进、空格等格局要求来进步代码的可读性。 10.可维护性准则(Maintainability Principle):代码应该是可保护的,通过良好的设计、正文等伎俩来进步代码的可维护性。 这些代码设计准则并不是强制执行的规定,而是倡议。在理论的开发中,依据我的项目的需要和理论状况抉择适合的准则,以保障代码的品质和易用性。

February 12, 2023 · 1 min · jiezi

关于设计原则:GRASP通用职责分配模式

在软件设计中构思对象的职责、角色和合作,须要用到一些准则和模式,GRASP九大准则 -> SOLID六大准则 -> GOF23种设计模式,GRASP处于最上层(更一般化的准则,不只利用于面向对象设计),SOLID准则再进一步细化,GOF再依据这些准则进一步的演绎出更具体的模式。 软件设计在实质上是为了管制复杂度,升高”软件的熵“。软件的复杂性在于除了要解决功能性需要,还要解决非功能性需要:可用性、可维护性、可扩展性、稳定性、高性能、数据一致性、容错性、可复用性、安全性、幂等性、兼容等等。 在常见的分层设计中,每一层都对下层屏蔽了外部的复杂度。同理,在类的设计中,类的复杂度也通过封装不蔓延到其余中央。 GRASP(General Responsibility Assignment Software Pattern),来源于:克雷·拉蒙.《Applying UML and Patterns》.1997,蕴含以下九个准则。 信息专家(Information Expert)定义:如果一个类领有执行某个职责所必须的信息,那么将这个职责调配给这个类。长处:升高了类间耦合,使信息(常识)没有蔓延进来。 创建者(Creator)谁应该负责创立一个类的实例?此模式答复了这个问题。定义:如果合乎上面的一个或者多个条件,则可将创立类A实例的职责调配给类B。1.B蕴含或聚合了A;2.B领有初始化A的数据,并把该数据在创立A的实例时传递给A;3.B记录A的实例;4.B频繁应用A;长处:升高了类间耦合,因为类B和类A曾经存在耦合,所以并没有减少额定的耦合。 低耦合耦合:评估一个零碎中各个元素(类,模块,子系统)之间的依赖水平。以下是一些耦合关系的体现:1.A具备一个B类型的属性(field);2.A调用了B的办法;3.A的办法蕴含对B的援用,比方办法参数为B,或返回类型为B;4.A是B的子类;5.B是一个接口,A实现了B接口;以上这些耦合关系越多代表耦合水平越高。这些关系简略地说就是A对B的“感知”。高耦合的毛病:一个类的批改会对其余类产生影响,容易产生脱漏和问题;零碎难以保护和了解,复用性也很差。升高耦合的一些办法:尽量减少对其余类的援用;进步办法和属性的拜访权限;尽量应用组合/聚合准则来代替继承;多态也是一种升高耦合的办法,调用者只须要晓得父类即可,不须要晓得所有子类型,升高了类型耦合。类间依赖是一种常见的耦合关系,尽量应用单向依赖,去除或弱化双向依赖,不应用循环依赖。 高内聚内聚:是对元素职责的相关性和集中度的度量。定义:即性能严密相干的职责应该放在一个类里,并共同完成无限的性能。与繁多职责和接口隔离准则是统一的。长处:类的性能高内聚后复杂性就升高了,保护老本也就随之升高;同时进步了复用性。低内聚的毛病:类要做许多不相干的工作,或须要实现大量的工作,导致类难以了解,难以复用,难以保护,常常被改变。如下图,非内聚的类(或模块)蕴含了多个不相干业务的性能,看起来比拟芜杂。对它进行职责拆分后,不同业务的性能放在不同的类(模块),高内聚之后的构造显得非常清晰。 控制器(Controller)定义:把接管或者解决零碎事件音讯的职责调配给一个类(这个类也能够是:子系统,或设施);一个控制器应该解决一类事件,控制器要把实现的性能委托给业务层Service,它只负责协调和管制业务流程。长处:控制器的外围是提供一个对立入口(比方外观模式),防止客户对系统外部进行耦合,很好的保护了职责边界。 多态定义:当相干抉择或行为随类型(类)变动而变动时,用多态操作为行为变动的类型调配职责。长处:多态是面向对象设计的基本操作,它使将来的变动能够通过扩大新的子类解决,合乎开闭准则。 纯虚构(Pure Fabrication)定义:为了反对良好的内聚和低耦合以及复用性,将一组相干职责调配给一个虚构的类(这个类不是问题域中的概念)。比方数据库操作DAO,service,handler等。长处:解耦,以及反对了高内聚和复用性。 间接(Indirection)定义:配职责给两头对象(中介)以协调组件或服务之间的操作,使得它们不间接耦合。长处:实现了类间的隔离和解耦。有一种风趣的说法:“大多数设计问题都能够通过减少一层来解决,如果不行就再加一层。而性能问题则刚好相同,缩小一层就能解决问题。” 受爱护的变动(Protected Variations)定义:找出预计有变动或不稳固的元素,为其创立稳固的“接口”而调配职责。即思考将来变动的扩展性,它是大多数程序设计的根底,是模式的根本动机之一,它使零碎可能适应和隔离变动。与ETC准则统一,与面向对象的开闭准则绝对应。

November 2, 2022 · 1 min · jiezi

关于设计原则:开闭原则OCP的理解与灵活应用

开闭准则可能是 SOLID 中最难了解、最难把握,同时也是最有用的一条准则。 之所以说这条准则难了解,那是因为,“怎么的代码改变才被定义为‘扩大’?怎么的代码改变才被定义为‘批改’?怎么才算满足或违反‘开闭准则’?批改代码就肯定意味着违反‘开闭准则’吗?”等等这些问题,都比拟难了解。之所以说这条准则难把握,那是因为,“如何做到‘对扩大凋谢、批改敞开’?如何在我的项目中灵便地利用‘开闭准则’,以防止在谋求扩展性的同时影响到代码的可读性?”等等这些问题,都比拟难把握。之所以说这条准则最有用,那是因为,扩展性是代码品质最重要的衡量标准之一。在 23 种经典设计模式中,大部分设计模式都是为了解决代码的扩展性问题而存在的,次要听从的设计准则就是开闭准则。开闭准则的英文全称是 Open Closed Principle(OCP)。它的英文形容是:software entities (modules, classes, functions, etc.) should be open for extension , but closed for modification。 用中文具体表述下就是:增加一个新的性能应该是,在已有代码根底上扩大代码(新增模块、类、办法等),而非批改已有代码(批改模块、类、办法等)。 如何了解“对扩大凋谢、对批改敞开”?咱们通过上面的例子来更好的了解“对扩大凋谢、对批改敞开”的含意。 初始示例这是一段 API 接口监控告警的代码。其中, AlertRule 存储告警规定,能够自在设置。Notification 是告警告诉类,反对邮件、短信、微信、手机等多种告诉渠道。NotificationEmergencyLevel 示意告诉的紧急水平,包含 SEVERE(重大)、URGENCY(紧急)、NORMAL(一般)、TRIVIAL(无关紧要),不同的紧急水平对应不同的发送渠道。public class Alert { private AlertRule rule; private Notification notification; public Alert(AlertRule rule, Notification notification) { this.rule = rule; this.notification = notification; } public void check(String api, long requestCount, long errorCount, long durationOfSeconds) { long tps = requestCount / durationOfSeconds; if (tps > rule.getMatchedRule(api).getMaxTps()) { notification.notify(NotificationEmergencyLevel.URGENCY, "..."); } if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) { notification.notify(NotificationEmergencyLevel.SEVERE, "..."); } }}下面这段代码非常简单,业务逻辑次要集中在 check() 函数中。当接口的 TPS 超过某个事后设置的最大值时,以及当接口申请出错数大于某个最大允许值时,就会触发告警,告诉接口的相干负责人或者团队。 ...

December 7, 2021 · 4 min · jiezi

关于设计原则:开放封闭原则不改代码怎么写新功能

再小的致力,乘以365都很显著前言上一篇文章,咱们讲了一个最根底的设计准则:繁多职责准则。这一讲,咱们来看下一个设计准则:凋谢关闭准则。 作为一名程序员,来了一个需要就要改一次代码,这种形式咱们曾经见怪不怪了,甚至曾经变成了一种下意识的反馈。批改也很容易,只有咱们依照之前的常规如法炮制就好了。 这是一种不费脑子的做法,却随同着长期的挫伤。每人每次都只改了一点点,然而,通过长期积攒,再来一个新的需要,改变量就要很大了。而在这个过程中,每个人都很无辜,因为每个人都只是遵循常规在批改。但后果是,所有人都受到了挫伤,代码越来越难以保护。 既然“批改”会带来这么多问题,那咱们能够不批改吗?凋谢关闭准则就提供了这样的一个新方向。 简介凋谢关闭准则是这样表述的: 软件实体(类、模块、函数)应该对扩大凋谢,对批改关闭。这个说法是 Bertrand Meyer 在其著述《面向对象软件结构》(Object-Oriented Software Construction)中提出来的,它给软件设计提出了一个极高的要求:不批改代码。 或者你想问,不批改代码,那我怎么实现新的需要呢?答案就是靠扩大。用更艰深的话来解释,就是新需要应该用新代码实现。 凋谢关闭准则向咱们形容的是一个后果,就是咱们能够不批改代码而仅凭扩大就实现新性能。然而,这个后果的前提是要在软件外部留好扩大点,而这正是须要咱们去设计的中央。因为每一个扩大点都是一个须要设计的模型。 解释举个例子,如果咱们正在开发一个酒店预订零碎,针对不同的用户,咱们须要计算出不同的房价。比方,普通用户是全价,金卡是 8 折,银卡是 9 折,代码写进去可能是这样的: class HotelService { public double getRoomPrice(final User user, final Room room) { double price = room.getPrice(); if (user.getLevel() == Level.GOLD) { return price * 0.8; } if (user.getLevel() == Level.SILVER) { return price * 0.9; } return price; }}这时,新的需要来了,要减少白金卡会员,给出 75 折的优惠,如法炮制的写法应该是这样的: class HotelService { public double getRoomPrice(final User user, final Room room) { double price = room.getPrice(); if (user.getLevel() == UserLevel.GOLD) { return price * 0.8; } if (user.getLevel() == UserLevel.SILVER) { return price * 0.9; } if (user.getLevel() == UserLevel.PLATINUM) { return price * 0.75; } return price; }}显然,这种做法就是批改代码的做法,每减少一个新的类型就要批改一次代码。然而,一个有各种级别用户的酒店零碎必定不只是房价有区别,提供的服务也可能有区别。可想而知,每减少一个用户级别,咱们要改的代码就铺天盖地。 ...

May 11, 2021 · 1 min · jiezi

关于设计原则:设计模式原则之单一职责原则SRP

人生没有白走的路,每一步都算数前言在面向对象的软件设计中,只有尽量升高各个模块之间的耦合度,能力进步代码的复用率,零碎的可维护性、可扩展性能力进步。面向对象的软件设计中,有23种经典的设计模式,是一套前人代码设计教训的总结,如果把设计模式比作文治招式,那么设计准则就好比是内功心法。罕用的设计准则有七个,本文将具体介绍繁多职责准则。 设计准则简介繁多职责准则:专一升高类的复杂度,实现类要职责繁多;凋谢敞开准则:所有面向对象准则的外围,设计要对扩大开发,对批改敞开;里式替换准则:实现凋谢敞开准则的重要形式之一,设计不要毁坏继承关系;依赖倒置准则:零碎抽象化的具体实现,要求面向接口编程,是面向对象设计的次要实现机制之一;接口隔离准则:要求接口的办法尽量少,接口尽量细化;迪米特法令:升高零碎的耦合度,使一个模块的批改尽量少的影响其余模块,扩大会绝对容易;组合复用准则:在软件设计中,尽量应用组合/聚合而不是继承达到代码复用的目标。这些设计准则并不说咱们肯定要遵循他们来进行设计,而是依据咱们的理论状况去怎么去抉择应用他们,来让咱们的程序做的更加的欠缺。 繁多职责准则就一个类而言,应该仅有一个引起它变动的起因,艰深的说,就是一个类只负责一项职责。此准则的外围就是解耦和加强内聚性。 如果一个类承当的职责过多,就等于把这些职责耦合在一起,一个职责的变动可能会减弱或者克制这个类实现其余职责的能力。这种耦合会导致软弱的设计。 长处: (1)升高类的复杂度; (2)进步类的可读性,进步零碎的可维护性; (3)升高变更引起的危险(升高对其余性能的影响)。 咱们来举一些简略的例子来阐明一下这个繁多职责准则 public class Animal { public void breathe(String animal) { System.out.println(animal + "生存在海洋上"); }}public class Test { public static void main(String[] args) { Animal animal = new Animal(); animal.breathe("牛"); animal.breathe("羊"); }}运行后果: 牛生存在海洋上羊生存在海洋上动物并不是都生存在海洋上的,鱼就是生存在水中的,批改时如果遵循繁多职责准则,须要将Animal类细分为陆生动物类Land,水生动物Water,代码如下:public class Land { public void breathe(String animal) { System.out.println(animal + "生存在海洋上"); }}public class Water { public void breathe(String animal) { System.out.println(animal + "生存在水里"); }}public class Test { public static void main(String[] args) { Land land = new Land(); land.breathe("牛"); land.breathe("马"); Water water = new Water(); water.breathe("鱼"); }}运行后果: ...

May 11, 2021 · 1 min · jiezi

关于设计原则:混合开发TDDDDD和BDD交集的值

目录TDD测试驱动开发 : Test-driven development,即测试驱动开发BDD行为驱动开发 : Behavior-driven development,即行为驱动开发DDD畛域驱动设计 : Domain-drive Design,畛域驱动设计测试驱动开发(TDD)是一种开发软件的过程,其中在编写代码之前先编写测试。一旦实现,开发人员将致力编写足够的代码以通过测试,而后开始重构。 域驱动设计(DDD)是一种将实现与一直倒退的模型分割在一起的开发方法。将我的项目的重点放在外围畛域(常识畛域),背地的逻辑上,并迫使技术和非技术方面之间进行合作以改良模型。行为驱动开发(BDD)是对TDD和DDD的改良,旨在通过放大沟通差距,增进对客户的理解并实现继续沟通来简化开发。简而言之,BDD是将业务需要与代码联合在一起的一种形式,使您可能从业务/最终用户的角度理解零碎的行为。 Hybrid Practice混合实际 TDDTest-driven development,即测试驱动开发。一种开发过程中利用办法。其思维为先依据需要形象接口,先编写测试用例,而后在开始编写开发代码。TDD的本意就是通过测试来推动整个开发的进行。 TDD说白了就是先写一小段性能的测试代码,测试失败后再写实现代码,测试胜利后接着迭代下一个性能。 TDD的长处: 进步性能的可测性测试比拟灵便测试用例覆盖率比拟高毛病: 因为是单元测试代码验证,非技术人员看不懂代码,比拟难验证性能是否贴合需要先写单元测试代码,测试用例须要常常调整,减少了开发人员的工作量和升高了开发效率BDDBehavior-driven development,即行为驱动开发。其目标是激励软件我的项目中的开发者、QA和非技术人员或商业参与者之间的合作。是从用户的需要登程,强调零碎行为。通过用自然语言书写非程序员可读的测试用例扩大了测试驱动开发方法,应用混合了畛域中对立的语言的母语语言来形容他们的代码的目标,让开发者得以把精力集中在代码应该怎么写,而不是技术细节上,而且也最大水平的缩小了将代码编写者的技术语言与商业客户、用户、利益相关者、我的项目管理者等的畛域语言之间来回翻译的代价. 目前支流的BDD测试框架为Cucumber ,反对多种编程语言。 cucumber反对的关键字: feature | "性能" |background | "背景" |scenario | "场景", "剧本" |scenario outline | "场景纲要", "剧本纲要" |examples | "例子" |given | "* ", "如果", "假如", "假设" |when | "* ", "当" |then | "* ", "那么" |and | "* ", "而且", "并且", "同时" |but | "* ", "然而" |given (code) | "如果", "假如", "假设" |when (code) | "当" |then (code) | "那么" |and (code) | "而且", "并且", "同时" |but (code) | "然而" |编程是通过应用以上的关键字来形容利用的性能,使非开发人员能疾速便捷的理解代码的性能。 ...

December 5, 2020 · 1 min · jiezi

DRYKISSYAGNI三原则的理解

软件三原则的个人理解在软件的设计当中前人已经总结了许多的设计原则和设计模式。例如SOLID,GRASP设计原则,这些原则都是基于面向对象设计总结而来的。而GOF23是基于许多常见的场景总结出了一套设计模式,在我们遇到类似的场景,都可以套用设计模式。而今天所讲到的软件三原则是适用于在软件设计的各个层面的。它不仅适用于面向对象的设计,也适用于面向过程的程序设计;不仅适用于类的设计,也适用于模块、子系统的设计。就连在项目架构运维部署中也适用于这一套简单的法则。 DRY - Don't Repeat Yourself第一条准则是千万不要重复你自身。尽量在项目中减少重复的代码行,重复的方法,重复的模块。其实许多设计原则和模式最本质的思想都是在消除重复。我们经常提起的重用性和可维护性其实是基于减少重复这一简单的思想。为什么现在微服务盛行呢?正是因为将系统中的服务进行抽取的话,便减少了重复。重复冗余在维护代码的时候将是非常困难的。DRY意味着系统内的每一个部件都应该是唯一的并且是不模糊的。我们可以通过应用单一职责接口隔离等原则尽量拆分系统,模块,类,方法·。使其每一个部件都是职责明确的并且可重用的。 KISS - Keep It Simple & Stupid第二条准则是保持简单易懂。从小到几行代码的写法大到整个系统的架构我们都应该保持简单易懂。高手高就高在可以将复杂的东西“简单”的实现出来。刚入行的时候,我总喜欢用三目运算符将复杂的逻辑用一句冗长的代码行写出来。后面才发现这是非常愚蠢的。到了重构或者需求变更的时候,连我自己写的代码我都看着非常费劲难以下手。所以我们应该致力于代码的可理解性。降低复杂度也意味着维护变得简单。Martin Flower在《重构》中有一句经典的话:"任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员。其实不光是程序,这个原则也可以延伸到产品的设计,业务的设计,项目结构的设计上。 YAGNI - You Ain’t Gonna Need It第三条准则是你将不会需要它。千万不要进行过度设计。我们经常会在开发当中尽可能的迎合未来可能的需求。而为了迎合某些产生概率极低的需求而设计的成本是非常高的,这种过度设计的收益非常低。可能你深思熟虑的设计花了不少时间成本,却在未来的两三年内这个设计却完全没有派上用场。一些设计是否必要,更多的应该基于当前的情况。而不是为了应对未来的各种变化,画蛇添足的设计。如果淘宝一开始就往日均交易上亿的情况进行设计的话,那么可能就不会有今天的淘宝了。因为创业公司的时间是非常宝贵的,比其他公司早一步退出新的服务就能抢占先机。并不是说淘宝不需要考虑以后交易量暴增的情况,而是不应该以当前日均交易才几万的情况下去设计编码日均交易上亿的项目。过度设计往往会延缓开发迭代的速度。 转自我的个人博客 vc2x.com

August 28, 2019 · 1 min · jiezi

JavaScript设计原则编程技巧及常用设计模式

【前言】最近阅读量了《JavaScript设计模式与开发实践》,收货颇丰,于是想写一点总结及感想 写一篇文章对于我的意义在于: 给别人讲述知识时可以发现自己掌握的是否牢固透彻,写的过程不断发现自己的不足,然后通过一些方式来解决问题,这也是一种学习过程;当然,写文章给别人看,也要从读者的角度出发,考虑他们想要从这篇文章获得什么,还有就是你想表达些什么给读者这种过程大概叫费曼学习法,图解:(图片来自网络,侵删) 这篇文章我想表达的是:学习设计原则设计模式的好处、介绍设计原则和设计模式、常用设计模式的实践、代码重构的具体方法、一些问题一些思考。你可以先读一遍带着疑问去阅读这本书籍或者阅读完书籍再来看这篇文章是否有助于你理解 一、为什么要学习设计原则、设计模式首先,设计原则、设计模式受用的目标人群我觉得是有一定的js基础且有一定的项目实践经历的开发者,不然的话,就算学习设计也是生搬硬套,收货甚微,当有了一定基础及实践之后,阅读本书之后有三种感觉: 你的某些代码就是书上的反例,醍醐灌顶的感觉你的某些代码已经实践了某些设计模式遵从了某些设计原则,但是你并不知道这样写代码是叫这个模式以及这个模式的全部优缺点或者你的代码还有更进一步优化的空间内心冷笑一声:哼 so easy... emmmmm,如果大佬还愿意继续阅读本文的话,希望大佬可以在评论区指点一二个人认为,JavaScript设计原则以及设计模式都只属于软件设计的一部分,但这意味着已经开始脱离了’API调用工程师‘的称号,开始接触编程思想,但是设计原则跟模式有限,只针对于代码层面。 打个比方:vue源码使用了xx模式,解决了xx问题,但是,在选择xx模式解决xx问题的背后又有多少思考????呢?简单猜测一下:一个框架or库 = 软件设计 + 其他框架优点借鉴 + 创新 + 编码 + 测试;本人水平有限,这是知识宏观的揣测一下实现一个框架or库需要的付出。请不要当真or较真 可能没人要你去写框架什么的,但是你负责的部分总是你来写来维护,不按套路出牌受伤的是自己举这个例子是想说明:学习一下设计原则设计模式是多么的有必要(强行解释,最为致命) 二、JavaScript常用设计原则我想提一个这本书的缺点,就是目录,设计模式都是要遵循设计原则的,而且很多设计模式章节都提到了设计原则,然而书的目录是最后一个大章节才说的设计原则,我觉得设计原则应该放在设计模式之前,所以建议先阅读设计原则再阅读设计模式,在理解上也会有帮助,至于第一章节的基础知识介绍,这个看各人情况可以选择要不要忽略 下面介绍常用的设计原则 1. 单一职责原则 SRP【定义】 单一职责原则的定义是‘引起变化的原因’,如果你有两个原因去修改一个方法,说明这个方法有两个职责,承担的职责越多你需要修改他的可能性就越大,修改代码总是件危险的事情,特别是两个职责耦合在一起的时候一句话概括:一个对象(方法)只做一件事【理解】 书上有介绍单一职责原则相关的模式,其实我觉得从原则去联系模式有点不合理,因为所有模式都会去遵从设计原则,知识侧重点不一样而已。所以我会在下个章节的模式里去联系原则,而说原则,我只想脱离模式单独去说原则的优缺点以及如何应用【优点】降低了单个类或者对象的复杂度,按照职责把对象分解成更小的粒度,这有助于代码的复用,也有利于进行单元测试。当一个职责需要变更的时候,不会影响到其他的职责。【缺点】增加编码复杂度,同时增加对象之间联系的难度 【应用】煮个栗子,js的组件化开发,有一种思想就是组件的原子化,把组件拆到不能再拆的粒度,是最好维护的。但事实真的是这样吗,组件多了,组件之间的联系也多了,就意味需要管理更多的组件及组件间的复杂关系,有时候明明两个组件像孪生兄弟一样分不开,你偏要拆散他们并且给他们一座桥梁去联系,这无疑比让他们在一起成为一个整体要难维护的多,而且颗粒度越小,代表着开发成本越大。 单一职责原则也是如此,这些思想其实建立在理想化的状态上,而实际情况往往不会与理想对等,所以实际项目使用中,要学会灵活多变,知道什么时候其实是可以违反原则的,借用书上的话: SRP 原则是所有原则中最简单也是最难正确运用的原则之一。要明确的是,并不是所有的职责都应该一一分离 一方面,如果随着需求的变化,有两个职责总是同时变化,那就不必分离他们。比如在 ajax 请求的时候,创建 xhr 对象和发送 xhr 请求几乎总是在一起的,那么创建 xhr 对象的职责和发送 xhr 请求的职责就没有必要分开。 另一方面,职责的变化轴线仅当它们确定会发生变化时才具有意义,即使两个职责已经被耦 合在一起,但它们还没有发生改变的征兆,那么也许没有必要主动分离它们,在代码需要重构的 时候再进行分离也不迟 个人认为这是理解最简单,实践最难的一个 // badvar createLoginLayer = (function(){ var div; return function(){ if ( !div ){ div = document.createElement( 'div' ); div.innerHTML = '我是登录浮窗'; div.style.display = 'none'; document.body.appendChild( div ); } return div; }})();// goodvar getSingle = function( fn ){ // 获取单例 var result; return function(){ return result || ( result = fn .apply(this, arguments ) ); }var createLoginLayer = function(){ // 创建登录浮窗 var div = document.createElement( 'div' ); div.innerHTML = '我是登录浮窗'; document.body.appendChild( div ); return div; };var createSingleLoginLayer = getSingle( createLoginLayer );var loginLayer1 = createSingleLoginLayer();var loginLayer2 = createSingleLoginLayer();alert ( loginLayer1 === loginLayer2 ); // 输出: true2. 最少知识原则 LKP单一职责原则说道:一个对象(方法)只做一件事;那代表着我们要创建更多的对象(方法)来分解一个之前比较大的对象(方法),那分解之后,对象(方法)是小了,好维护好扩展了,但是对象之间的联系缺越来越多了,有两个对象非常耦合,怎么办 ...

July 4, 2019 · 4 min · jiezi

Java设计模式六大原则

1、单一职能原则(Single Responsibility Principle, SRP)定义There should never be more than one reason for a class to change.应该有且仅有一个原因引起类的变更 换言之,也就是一个接口或类只有一个职责 好处类的复杂性降低,实现什么职责都有清晰明确的定义;可读性提高,复杂性降低,那当然可读性提高了;可维护性提高,可读性提高,那当然更容易维护了;变更引起的风险降低,变更时必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。最佳实践职责的划分很难,要想完全符合单一职责的设计更难,原则是接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化 2、里氏替换原则(LiskovSubstitution Principle,LSP)继承的利与弊优点 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;提高代码的重用性;子类可以形似父类,但又异于父类;提高代码的可扩展性,实现父类的方法就可以“为所欲为”了;提高产品或项目的开放性。缺点 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大段的代码需要重构。定义If for each object o1 of type S there is an object o2 oftype T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 issubstituted for o2 then S is a subtype of T.如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。 以上是LSP"最正宗"的定义,但是可能不太容易理解,它还有另外一种相对清晰明确的定义: Functions that use pointers or references to base classes must be able to useobjects of derived classes without knowing it.所有引用基类的地方必须能透明地使用其子类的对象。 ...

June 10, 2019 · 2 min · jiezi

我对SOLID的理解

超前的设计或者过度的设计都不是良好的设计,很多时候我们等到代码在第一次变化的时候可以及时作出反应就够了单一责任原则(The Single Responsibility Principle )根据实际情况,拿捏需求的拆分力度,根据拆分的块,来设计对应的类单一责任原则:我们设计一个类的时候,应该尽量把类的职责单一化;那么我们拿到需求的时候,应该对需求进行分析,再拆分职责,再设计对应的类。在实际工作中吧,其实大部分的程序猿都知道这个道理,主要的阻力是开发时间(偷懒),为了方便处理就不搞太多类了;还是有个原因,就是如果拆分力度大,拆分的太细,那么可能会出现一个简单的需求,你写了好几个类,这样也不是可取的,这种有点过度设计了,没有必要。举个例子吧,我是做iOS的,就说说界面相关的需求吧,比如说现在产品的需求是做一个word功能的属性面板,里面有几个模块:选择字体,设置对齐方式,设置字体颜色,每个模块都是列表(tableView),那么在开发前,就应该拆分好,比如说属性面板(容器类),三个子模块(对应三个类),这样来开发,这就是单一责任的应用了再举个例子,比如这时候要设计一个工厂类,工厂类需要生产A,B产品,还有一个原料的预加工,现在的需求是原料的预加工,A和B都是一样的,也就是说A和B可以共用这个方法,那么这三个功能就都堆在工厂类里面了。这样是违背了单一责任原则了,但是如果需求确实这边简单,比较小需求,那么这样来开发我觉得也没有问题,毕竟对于大部分怼业务的程序猿来说,又快又稳的出货才是王道。如果第二期需求来了,要求改动A产品的预加工方式,那么这时候,A和B就不可以公用一个预加工方法了,那么这个时候,就是要及时作出重新设计的考虑了,而且这时候的考虑可以多考虑一下未来可能的改动。说回刚来产品要求改A产品的预加工需求,如果我们改了工厂类的预加工方法,那么B产品就受影响了。这个就说明了单一责任原则的重要性了,可能降低以后版本迭代中,改动的影响面。对于这个例子,可能一些同学觉得这个很简单就知道我改动预加工方法会影响到B产品,问题是,在比较大项目,复杂的项目中,有时候改动了一个地方,影响到另一个地方是很难发现的,测试阶段发现还好,有时候可能不是很明显,也不是严重的影响,有可能上线了好久才偶然间发现除了类需要遵循单一职责原则,方法也同样需要,一般方法内的代码不能太多开闭原则(The Open Closed Principle)用抽象构建框架,用实现扩展细节开闭原则:就是写的代码即要有开放性,也要有封闭性,比如现在写一个加法需求,如果一开始就写一个加法类,那么后期扩展,有减法,乘法,除法等,那么是不是继续创建新的类,那么这些加减乘除是不是应该会存在共用的东西,那么就可以抽成一个计算类,这个计算类可以做一些共用的约束,比如计算类可以有formula公式方法,result计算结果的方法,然后加减乘除继承于计算类,重写对应的方法即可。那么这样的操作,就是对计算类封闭,但是计算类又对外开放,比如继承他,去实现一些细节问题所以开闭原则说的就是这个意思,用抽象构建框架,用实现扩展细节,比如抽象类(计算类),实现细节由加减乘除子类去做。需要说明的是,对修改关闭不是说软件设计不能做修改,只是尽量不要做不必要的修改。怎么才能做到呢?那就是有相应的扩展性。其实,软件有相应的扩展性是好处,但是不能说每个地方都有扩展。反而造成了代码的臃肿。所以这里的扩展与修改关闭是有限制的。这个结合实际的工作开发,如果我们遇到的需求,都考虑弄个抽象类,都自己考虑了以后的一堆扩展,不是说这个考虑不好,只是在开发上浪费了时间,有可能你的架构,在未来很长时间都没有用上,那么这就形同臃肿的架构设计,所以实际开发,还是要按照实际情况去处理,不要为了达到开闭原则写开闭原则。还有抽象层尽量保持稳定,尽量不修改,因为我们在开发中,修改老旧代码,评估最多的是影响面,如果动到了抽象层,意味着影响面很大里氏替换原则(Liskov Substitution Principle)规范子类的书写规则,实现父类抽象方法,不能覆盖父类的具体方法,以此达到父类的方法不被覆盖和父类可以出现的地方,子类就能出现(意思就是比如一个方法的参数是传父类类型,那么这时候传子类进去,也得是没有问题的)我们设计基类的时候,应该尽量做到基类是抽象的,尽量抽象,如果一个基类的功能够完善,那么这个应该定义为具体类,而不是基类,因为越完善越具体的类,以后子类继承他,子类的扩展性就越差。这就违背了开闭原则。 至于里氏替换原则,说的就是规范一些子类写法的规则,比如子类可以实现父类的抽象方法,但是不能覆盖父类的具体方法子类可以增加自己特有的方法当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。第三和四点不太理解,这有一个详细的问题,但是我没看懂(https://blog.csdn.net/qq_3496…说简单点就是开闭原则就是父类尽量抽象,子类扩展,里氏替换原则就是子类实现的细节怎么来写,怎么规范,还有继承必须确保父类所拥有的性质在子类中仍然成立。接口隔离原则(Interface Segregation Principle)接口尽量细化,不要臃肿,这里接口的意思就是oc的代理,java的interface比如现在接口1有5个方法,类A依赖接口1的1-3个方法,类B依赖接口1的3-5个方法;那么类A依赖接口1就必须实现这5个方法,但是4、5方法对于类A来说是完全没有必要实现的,所以这样的接口1设计就是臃肿的,所以我们应该细化他,变成两个接口,对应类A和B在oc中代理方法有可选的,那么似乎就没有这个问题了,这么看也确实没有,但是@protocol设置@optional的意思是,可以实现,也可以不实现,是非必须的意思。接口隔离原则就是完全不必要实现的,这种情况就需要考虑拆分,细化接口依赖倒置原则(Dependency Inversion Principle)面向抽象(接口)编程接口,抽象,意思就是定义一种规范,协议,自己不实现具体的代码,只是指明了大方向的意思,比如一个公司的老板就是接口,就是抽象的,因为他不用做细节的东西,但是他指定的方向,规范细节就是具体的实现比如在iOS中,协议就是接口,抽象类的抽象方法也是接口,我们常说要面向接口编程,而不是面向实现编程,因为接口是文档的,细节的实现是多变的。我们的编程是追求稳定维护程序的,所以我们要面向接口编程举个例子初级程序猿类primary方法:我的工资是1万薪酬管理类方法:claculate:(primary *)pri那么薪酬管理类可以计算出初级程序猿的工资了,但是有个问题,如果要计算中级程序猿的话,那薪酬管理类不就得再添加方法,因为claculate:(primary *)pri方法的入参是初级程序猿类,那这样不行,以后越来越多岗位不就得总去修改薪酬管理类。所以这时候,我们要面向接口编程定义一个协议@protocol EmployeeDelegate <NSObject>- (void)calculateSalary; @end薪酬管理类- (void)claculate:(id<EmployeeDelegate>)pri;这样每个岗位都实现代理方法,然后薪酬管理类的入参是实现了对应代理方法的类,这样就不用新增岗位,就去改管理类的代码了,这就是面向接口编程的一个例子了总结单一责任原则是最基本的编程要求,一般我们主要一个类一个责任,一个方法一个责任这样写编写代码。关于代码整洁的话,我的理解就是做到抽取代码,方法内容简洁,对应的方法内做对应的事,这样可以方便以后的查找,维护。开闭原则就是说要用抽象搭建框架,实现扩展细节,细节交给具体类或者子类来处理和扩展里氏替换原则就是规范子类的规范;接口隔离原则,接口(这里的接口指oc的协议)细化,不要臃肿;依赖倒置原则,就是面向接口编程我对SOLID之间关系的理解:单一责任原则是最基本的编程原则;开闭原则是整个程序架构的最终目标,里氏替换原则、接口隔离原则,依赖倒置原则都是为了实现开闭原则

March 31, 2019 · 1 min · jiezi

设计原则

何为设计《Unix/Linux设计哲学》中讲到的一些设计准则:设计准则小即是美让每个程序只做好一件事快速建立原型(先满足基本需求,再后续升级)舍弃高效率而采取可移植性采用纯文本来存储数据(可读性好)充分利用软件的杠杆效应(软件复用)使用shell脚本来提高杠杆效应和可移植性避免强制性的用户界面(用户界面应该是其中的一小部分,内存占用,交互复杂,效率低)让每个程序成为过滤器小准则允许用户定制环境尽量使操作系统内核小而轻量化使用小写字母并尽量简短沉默是金各部分之和大于整体寻求90%的解决方案单一职责原则一个程序只做好一件事如果功能过于复杂就拆分开,保持每个部分独立开放封闭原则对扩展开放,对修改封闭增加需求时,扩展新代码,而不是在原有代码上修改李氏置换原则子类能覆盖父类父类能出现对地方子类都能出现js中使用较少接口独立原则保持接口的单一独立,避免出现“胖接口”JS中没有接口,使用较少类似于单一职责原则,这里更关注接口依赖倒置原则面向接口编程,依赖抽象而不依赖于具体使用方法只关注接口而不关注具体类的实现JS中使用较少参考慕课网JavaScript设计模式系统讲解与应用

March 11, 2019 · 1 min · jiezi