DRY 全称:Don't Repeat Yourself (摘自wikipedia),是指编程过程中不写重复代码,将袜慎余能够公共的部分抽象出来,封装成工具类或者用“abstraction”类来抽象公有的东西,降低代码的耦合性,这样不仅提高代码的灵活性、健壮性告滚以及可读性,也方便后期的维护或者修改。
扩展资料:
DRY原则特指在程序设计以及计算中避免重复代码,因为这样会降低灵活性、简洁性,并且可能导致代码之间的矛盾,DRY是Andy Hunt 和 Dave Thomas's 的《 The Pragmatic Programmer 》孝含书中的核心原则。
参考资料:DRY原则--百度百科
作者:oec2003
公众号:不止dotNET
本文继续来介绍接口隔离原则(ISP)和依赖倒置原则(DIP),这两个原则都和接口和继承有关。文章最后会简单介绍几个除了 SOLID 原则之外的原则。
提起接口,开发人员的第一反应可能是面向对象编程语言中的 interface ,但接口更广义的理解会包含:
不管是上面的哪一种,要想设计好,就需要用到接口隔离原则了。
接口隔离原则的定义是:
接口被设计出来后,就会有地方对接口进行调用,调用的地方希望接口中提供的方法都是他需要的,所以在接口设计的时候,需要考虑应该将哪些方法放入其中,让调用者使用,这就是对定义的解释。
相反,如果不精心设计,接口就会变得越来越庞大,会带来两个问题:
1、在一个更高层的接口中添加一个方法只是为了某一个子类使用,所有的子类都必须对其实现,或提供一个默认实现;
2、接口中包罗万象,调用者可能会误用其中的方法。
举个例子:我们现在正在开发 SaaS 产品,里面会涉及到对租户的 *** 作,比如租户需要注册、登录等,抽象成接口代码如下:
上面的 *** 作是针对租户这个角色的,现在有新的需求来了,对于 SaaS 厂商的管理员来说,希望能禁用租户,一种偷懒的做法就是直接在 ITenant 接口中添加禁用的方法,如下:
上面的代码就违反了接口隔离原则,因为在普通租户的使用场景下,并不希望能调用到 Diabled 方法,正确的做法是将这个方法抽象到一个新的接口中,如下:
可以看出来,改造之后,每个接口的职责更加单一了,好像跟单一职责有点类似,仔细想想,还是有些区别,单一职责原则针对的是方法、类兆扒和接口的设计。而接口隔离原则更侧重于接口的设计告做,另一方面就是思考的角度不同,在上面例子中,按照普通租户和管理员两种不同角色的维度来思考并进行拆分。
这个原则的名字中有两个关键词「依赖」和「倒置」,先来看看这两个词是什么意思?
依赖:在面向对象的语言中,所说的依赖通常指类与类之间的关系,比如有个用户类 User 和日志类 Log , 在 User 类中需要记录日志,就需要引入日志类 Log,这样 User 类就对 Log 类产生了依赖,代码如下:
倒置:有依赖的倒置,那肯定就有正常的依赖,我们正常的编程思维都是从上而下来编写业务逻辑的,遇到分支就写 if ,遇到循环就写 for ,需要创建对象就 new 一个,就像上面的代码,上面的代码就是一种正常的依赖。User 类依赖了 Log 类,如果倒置了,那就是 User 类不再依赖 Log 类了,下面会进一步来解释。
正常的依赖会带来的问题是:User 类和 Log 类高度耦合,当有一天我们想使用 NLog 或者 Serilog 替换 Log 类时,就需要改动 User 类,说明日志类的实现是不稳定的,而依赖一个不稳定的东西,从架构设计的角度来看,不是一个好的做法。解决此问题就需要用到依赖倒置原则。
先来看看依赖倒置原则的定义:
什么是高层模块?什么是低层模块?按照上面的代码示例,User 类是高层模块,Log 类是低层模块,二者都要依赖于抽象,就需要提取接口了:
调整后的代码 User 类中依赖变成了 ILog 接口,日志的实现类 Log 也依赖 ILog 接口,即从 ILog 接口继承而来,现在都是依赖 ILog 接口,这就是依赖倒置。
当想要将日志组件替换为 NLog 时,只需要创建一个新的类 NLogAdapter 类继承 ILog 接口,在 NLogAdapter 类中引入 NLog 组件。
这样,当日志组件替换的时候,User 类就不用修改了,因为 User 类的构造函数中使用的是 ILog 接口来接收的日志组件的对象,那到底是谁决定传递 Log 对象还是 NLogAdapter 对象呢?这就要引入一个新的概念叫「依赖注入」。
关于依赖注入可以看我之前写的两篇文章:
依赖倒置是一种架构设计思想,指导架构层面的设计,依赖注族友昌入则是一种具体的编码技巧,用来实现这种设计思想。
除了 SOLID 五大原则之外,还有一些原则也在指引我们设计好的代码架构方面发挥着作用:
KISS 的全称是:Simple and Stupid ,该原则就是告诉我们,在设计时要尽量保持简单,大道至简嘛。这里的简单不完全是指代码的简洁。现在已经不是单打独斗的时代,大部分情况下开发人员都是在一个团队中协同工作,所以我认为对简单的理解可以分为:
将复杂的东西能够深入浅出,做到简单、简洁,这是能力的体现。
YAGNI 的全称是:You Ain’t Gonna Need It。直译就是:你不会需要它。核心思想就是指导我们不要做过度设计。
1、当我们能识别到代码的变化点的时候,可以预留扩展点,但不要提前做复杂的实现;
2、持续重构来优化代码,而不是一开始就提取各种通用方法,例如一个私有函数只有一个调用的时候,就放在类里面,离调用者最近的地方,当有不止一处都会使用时,再考虑重构来进行通用方法的抽取。
过度设计会浪费资源,让代码复杂度变大,难以阅读和维护。
DRY 的全称是:Don’t Repeat Yourself ,就是不要重复自己,提升代码的复用性,告别 CV 大法。
很多初级程序员都喜欢面向 Ctrl+C、Ctrl+V 编程,当需求变化的时候,很容易就遗漏一些场景,但即便是复制粘贴也不完全都是违反 DRY 。
代码的重复有两种情况:
1、代码的逻辑重复,语义也重复:这种违反了 DRY ,需要进行重构;
2、代码的逻辑重复,语义不重复:在某个阶段,两段代码逻辑是相同的,但其实是两种不同的应用场景,语义不一样,就没有违反 DRY。如果对这种代码进行重构提取成公共方法,随着业务发展,两种不同的场景独立演化了,稍不注意,代码中就会出现各种 if 判断,影响可读性和可维护性。
LOD 全称是:The Least Knowledge Principle ,也被称之为迪米特法则。该法则有两条指导原则:
1、不该有直接依赖关系的类之间,不要有依赖;
2、有依赖关系的类之间,尽量只依赖必要的接口。
其实就是一直流传的代码要高内聚、低耦合,单一职责和接口隔离想要表达的也是这个意思,区别只是侧重点有所不同:
各种原则之间相辅相成,有很多只是有些细微的差别,慢慢理解原理,才能以不变应万变。
面向对象设计原则是OOPS编程的核心,学习面向对象编程像“抽象”、“封装”、“多态”、“继承”等基础知识是重要的,但同时为了创建简洁、模块化的设计,了解这些设计原则也同等重要。
(设计原则)底线是永远追求高内聚、低耦合的编码或设计。Apache和Sun的开源代码是学习和OOPS设计原则的良好范例。它们向我们展示了,设计原则在编程中是如何使用的。JDK使用了一些设计原则:BorderFactory类中的工厂模式、Runtime类中的单例模式、.io类中的装饰器模式。顺便说一句,如果您真的对编码原则感兴趣,请阅读JoshuaBloch的Effective,他编写过API。我个人最喜欢的关于面向对象设计模式的是KathySierra的HeadFirstDesignPattern(深入浅出设计模式),以及其它的关于深入浅出面向对象分析和设计。这些书对编写更好的代码有很大帮助,充分利用各种面向对象和SOLID的设计模式。
虽然学习设计模式(原则)最好的方法是正滑悔现实中的例子和理解违反设计原则带来的不便,本文的宗旨是向那些没有接触过或正处于学习阶段的程序员介绍面向对象设计原则。
DRY_Don’trepeatyourself
我们第一个面向对象设计原则是:DRY,从名称可以看出DRY(don’trepeatyourself)意思是不写重复代码,而是抽象成可复用的代码块。如果您有两处以上相同的代码块,请考虑把它们抽象成一个单独的方法;或者您多次使用了硬编码的值,请把它们设置成公共常量。这种面向对象设计原则的优点是易于维护。让闷重要的是不要滥用此原则,重复不是针对代码而是针对功能来说。它的意思是,如果您使用通用代码来验证OrderID和SSN,这并不意味着它们是相同的或者他们今后将保持不变。通过把通用代码用于实现两种不同的功能,或者您把这两种不同的功能密切地联系在一起;当您的OrderID格式改变时,您的SSN验证代码将会中断。所以要当心这种耦合,而且不要把彼此之间没有任何关系却类似的代码组合在一起。
封装经常修改的代码
EncapsulateWhatChanges
在软件领域永远不变的是“变化”,所以把您认为或怀疑将来要被修改的代码封装起来。这种面向对象设计模式的优点是:易于测试和维护恰当封装的代码。如果您在用编程,那么请遵守以下原则:变量和方法的访问权限默认设置为私有,并且逐步放开它们的访问权限,例如从“private”到“protected”、“notpublic”。中的一些设计模式使用了封装,工厂设计模式就是一个例子,它封装了创建对象的代码而且提供了以下灵活性:后续生成新对象不影响现有的代码。
打开/关闭设计原则
OpenClosedDesignPrinciple
类、方法/函数应当是对扩展(新功能)开放,对修改闭合。这是另外一个优雅的SOLID设计原则,以防止有人修改通过测试的代码。理想情况下假如您添加了举正新功能,那么您的代码要经过测试,这就是打开/关闭设计原则的目标。顺便说一句,SOLID中的字母“O”指的是打开/关闭设计原则。
单一职责原则
SingleResponsibilityPrinciple(SRP)
单一职责原则是另外一个SOLID设计原则,SOLID中的字母“S”指的就是它。按照SRP,一个类修改的原因应当有且只有一个,或者一个类应当总是实现单一功能。如果您在中的一个类实现了多个功能,那么这些功能之间便产生了耦合关系;如果您修改其中的一个功能,您有可能就打破了这种耦合关系,那么就要进行另一轮测试以避免产生新的问题。
依赖注入/反转原则
DependencyInjectionorInversionprinciple
不要问框架的依赖注入功能将会给你带来什么益处,依赖注入功能在spring框架里已经很好的得到了实现,这一设计原则的优雅之处在于:DI框架注入的任何一个类都易于用模拟对象进行测试,并且更易于维护,因为创建对象的代码在框架里是集中的而且和客户端代码是隔离的。有多种方法可以实现依赖注入,例如使用字节码工具,其中一些AOP(面向切面编程)框架如切入点表达式或者spring里使用的代理。想对这种SOLID设计原则了解更多,请看IOC和DI设计模式中的例子。SOLID中的字母“D”指的就是这种设计原则。
优先使用组合而非继承
ForCompositionoverInheritance
如果可以的话,要优先使用组合而非继承。你们中的一些人可能为此争论,但我发现组合比继承更有灵活性。组合允许在运行时通过设置属性修改一个类的行为,通过使用多态即以接口的形式实现类之间的组合关系,并且为修改组合关系提供了灵活性。甚至Effective也建议优先使用组合而非继承。
里氏替换原则
LiskovSubstitutionPrincipleLSP
根据里氏替换原则,父类出现的地方可以用子类来替换,例如父类的方法或函数被子类对象替换应该没有任何问题。LSP和单一职责原则、接口隔离原则密切相关。如果一个父类的功能比其子类还要多,那么它可能不支持这一功能,而且也违反了LSP设计原则。为了遵循LSPSOLID设计原则,派生类或子类(相对父类比较)必须增强功能,而非减少。SOLID中的字母“L”指的就是LSP设计原则。
接口隔离原则
接口隔离原则指,如果不需要一个接口的功能,那么就不要实现此接口。这大多在以下情况发生:一个接口包含多种功能,而实现类只需要其中一种功能。接口设计是一种棘手的工作,因为一旦发布了接口,您就不能修改它否则会影响实现该接口的类。在中这种设计原则的另一个好处是:接口有一个特点,任何类使用它之前都要实现该接口所有的方法,所以使用功能单一的接口意味着实现更少的方法。
编程以接口(而非实现对象)为中心
编程总是以接口(而非实现对象)为中心,这会使代码的结构灵活,而且任何一个新的接口实现对象都能兼容现有代码结构。所以在中,变量、方法返回值、方法参数的数据类型请使用接口。这是许多程序员的建议,Effective以及headfirstdesignpattern等书也这样建议。
代理原则
不要期望一个类完成所有的功能,电脑培训http://www.kmbdqn.com/认为可以适当地把一些功能交给代理类实现。代理原则的典范是:中的equals()和hashCode()方法。为了比较两个对象的内容是否相同,我们让用于比较的类本身完成对比工作而非它们的调用方。这种设计原则的好处是:没有重复编码而且很容易修改类的行为。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)