设计模式之设计原则

1. 设计原则

​ 无论你是在大型团队中工作还是在单人项目中工作,软件开发都不仅仅是编写代码。构建应用程序的方式对软件应用程序的成败有很大的影响。当我们谈论一个成功的软件应用程序时,不仅讨论应用程序如何执行它应该做的事情,还讨论在开发它时付出了多少努力,以及它是否易于测试和维护。如果没有以正确的方式完成,那么暴涨的开发成本将会导致没人能接受这个应用程序。

​ 创建软件应用程序是为了满足不断变化和发展的需求。一个成功的应用程序还应该提供一种简单的方法来扩展它以满足不断变化的期望。幸运的是,我们不是第一个遇到这些问题的人。有一些问题已经被开发人员所发现并总结了解决方案。如果在设计和开发软件时应用一组面向对象的设计原则和模式,则可以避免或解决这些常见问题。

面向对象的设计原则也被称为SOLID。在设计和开发软件时可以应用这些原则,以便创建易于维护和开发的程序。SOLID最初是由Robert C.Martin所提出的,它们是敏捷软件开发过程的一部分,SOLID原则包括:

  • 单一职责原则(SRP)
  • 开闭原则(OCP)
  • 里氏替换原则(LSP)
  • 接口隔离原则(ISP)
  • 依赖倒置原则(DIP)

2. 单一职责原则

单一职责原则是一种面向对象的设计原则,该原则指出软件模块应该只有一个被修改的理由。在大多数情况下,编写JAVA代码时都会将单一职责原则应用于类。

单一职责原则可被视为使封装工作达到最佳状态的良好实践,更改的理由是需要修改代码。如果类需要更改的原因不止一个,那么每个类都可能引入影响其他类的更改。当这些更改单独管理但影响同一模块时,一系列更改可能会破坏与其他更改原因相关的功能。另一方面,每个更改的职责或理由都会增加新的依赖关系,使代码不那么健壮,更难以修改。

  • 比如一个数据模型类UserPO类的定义如下:

在这里插入图片描述

  • 假如使用数据库来持久化,需要提供增删改查的持久化操作,不得不对UserPO类做出如下改进:

在这里插入图片描述

在这种情况下,UserPO类不仅会封装数据模型的特征和行为,还会封装数据库的持久化行为。UserPO类的设计从最初的单一职责变成了两个职责,这将使我们的类更加难以维护,因为代码是紧密耦合的。UserPO类将取决于数据库,如果想要更改数据库系统,我们必须更改UserPO类中对数据库的持久化操作,这可能会在UserPO类的逻辑中产生错误。

  • 解决办法就是保持单一职责,使用两个类来分担职责,一个类负责描述数据模型,一个类负责数据持久行为,定义如下:

在这里插入图片描述

使用UserPO类承担数据模型描述职责,使用UserDao类承担数据持久职责,UserDao类依赖UserPO,UserDao类的更改不影响UserPO类。

3. 开闭原则

开闭原则描述为**模块、类和函数应该对扩展开放,对修改关闭,**应用此原则将有助于我们开发复杂而稳健的软件。我们必须明白:

开发的软件正在构建一个复杂的结构,一旦我们完成了它的一部分,不应该再修改它,而是应该在它的基础之上继续建设。软件开发也是如此,一旦我们开发并测试了一个模块,如果想要改变它,不仅要测试正在改变的功能,还要测试它负责的整个功能。这涉及许多额外的资源,这些资源可能从一开始就没有估算过,也会带来额外的风险,一个模块中的更改可能会影响其他模块或整体上的功能。

最好的办法是尝试在完成后保持模块不变,并通过继承和多态扩展来添加新的功能。开闭原则是最重要的设计原则之一,是大多数设计模式的基石。

4. 里氏替换原则

Barbara Liskov指出,派生类型必须完全可替代其基类型,里氏替换原则与子类型多态密切相关。基于面向对象语言中的子类型多态,派生对象可以用其父类型替换,例如:

Object content = new String("Hello World");

里氏替换原则声明:在设计模块和类时,必须确保派生类型从行为的角度来看是可替代的。当派生类型被其父类型替换时,其余代码就像它是子类型那样使用它。从这个角度来看,派生类型应该像其父类型那样表现,不应该破坏它的行为,这称为强行为子类型。

里氏替换原则最有名的例子是**“正方形不是长方形”**,当然生活中也有很多类似的例子,例如企鹅、鸵鸟和几维鸟从生物学的角度来划分它们属于鸟类,但是从类的继承关系来看,由于它们不能继承“鸟”会飞的行为,所以它们不能定义成“鸟”的子类。

鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行,假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然拿燕子来测试这段代码结果是正确的,能计算出所需要的时间。但是由于**“几维鸟不是鸟”**如果拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,明显不符合预期。

5. 接口隔离原则

接口隔离原则指出应该使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。接口隔离原则减少了代码耦合,使软件更加健壮,更易于维护和扩展。接口隔离原则最初是由Robert C.Martin提出的,他意识到如果接口隔离原则被破坏,客户端被迫依赖他们不使用的接口时,代码就会变得紧密耦合,几乎不可能为其添加新功能。

在这里插入图片描述

现在需要实现一个名为Mechanic的机械师类提供维修汽车行为(repairCar),机械师的维修依赖于汽车,但是汽车的定义却不仅仅是维修行为(repair)还有出售行为(sell)。吉普车(Jeep)实现了汽车的生产标准,提供了维修和出售行为,但是Jeep的行为超出了机械师的需要。

这其实是一个糟糕的设计,如果我们想要进行汽车的替换就不得不考虑机械师的维修行为是否要做出更改,这就违背了开闭原则。为了解决这种不必要的误解,我们需要对汽车接口进行拆分,拆分如下:

在这里插入图片描述

6. 依赖倒置原则

高级模块不应该依赖低级模块,两者都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖抽象。由于在软件设计中,细节具有多变性,而抽象层相对稳定,因此以抽象为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定得多,抽象指的是接口或者抽象类,细节指的是具体的实现类。

在这里插入图片描述

车辆服务类(VehicleService)所提供的的售卖、维修、测试、使用等行为不应该依赖于车辆(Vehicle)的具体实现自行车(Bike)、汽车(Car)和卡车(Truck),而应该依赖于抽象的汽车(Vehicle)。

7. 迪米特法则

设计原则其实很多,SOLID并不能囊括所有的设计思想比如还有迪米特法则,所谓的设计没有谁比谁优越,有的只是看这种设计是否符合我们的需要,设计的过度其实也会造成软件程序的复杂性,利弊的平衡是设计者值得去考量的一个问题。

迪米特法则又叫做最少知识原则,只与你的直接朋友交谈,不跟“陌生人说话”。如果两个软件实体无需直接通信,那么就不应该发生直接的相互作用,可以通过第三方转发调用,其目的是降低类之间的耦合,提高模块的相对独立性。

在迪米特法则中,“朋友”可以理解为当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象都和当前对象存在关联、聚合或组合的关系,可以直接访问这些对象的方法。

例如:

在这里插入图片描述

明星(Star)由于全身心投入艺术,所以很多日常事务由经纪人负责处理,比如与粉丝见面(meeting)、与公司洽谈合作业务(business)。经纪人(Angent)是明星的朋友适合直接通信,而粉丝(Fans)和公司(Company)不是明星需要重点关注的,因而通过经纪人统一交流。
、与公司洽谈合作业务(business)。经纪人(Angent)是明星的朋友适合直接通信,而粉丝(Fans)和公司(Company)不是明星需要重点关注的,因而通过经纪人统一交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值