本文共 2635 字,大约阅读时间需要 8 分钟。
开闭原则是软件设计中最基本且重要的原则之一。它的核心思想是:一个软件模型实体(如类、模块或函数)应该对扩展开放,对修改关闭。也就是说,系统应该能够轻松地在不改变系统结构和行为的情况下进行扩展。但一旦需要对现有功能进行修改时,系统应该能够以最小的代价进行调整。
举个例子,假设我们有一个4S店的销售系统。销售员的主要职责是卖车。系统最初的设计可能只有一个sellCar方法,直接返回车辆的价格。然而,随着双十一促销活动的到来,销售员需要在卖车时应用折扣。直接在sellCar方法中增加折扣逻辑显然不够灵活,会导致整个销售流程难以维护。
为了应对这种需求,我们可以引入一个DiscountCar类继承自Car,并在其中实现特定的折扣逻辑。这样,原来的销售系统只需要调用Car接口的sellCar方法,就能自动使用DiscountCar的实现,从而支持折扣功能,而不会对原有的系统产生影响。
依赖倒置原则(Dependency Inversion Principle, DIP)强调的是:一个软件单元应尽可能依赖于抽象而非具体的实现。具体来说,高层次的模块不应该直接依赖于低层次的模块,而是应该依赖于抽象的接口或类。
回到4S店的例子,假设销售不仅需要卖车,还需要销售其他商品。直接在sellCar方法中增加卖玻璃水或防冻液的功能,会让方法过于臃肿,难以维护。为了解决这个问题,我们可以引入一个Any接口,定义一个sell方法,参数类型为Any。具体的商品实现可以通过不同的类(如Car、Glass、Antifreeze等)来实现Any接口的sell方法。
这样一来,销售系统只需要调用any.sell()方法,就能根据具体的商品类型执行相应的业务逻辑,而不需要关心商品的具体实现细节。
职责单一原则(Single Responsibility Principle, SRP)指出,每个类或模块应该只有一个明确的职责。一个类或模块不应该承担多个不同的职责。
在4S店的例子中,销售员不仅需要卖车,还需要唱歌、跳舞、说rap等多种技能。我们可以通过接口的方式将这些技能独立出来,并让销售员实现多个接口。例如,Sing、Jump、Rap等接口,每个接口代表一种不同的技能。销售员类Seller则需要实现这些接口。
这样一来,系统的结构会更加清晰,技能的组合也更加灵活。例如,可以有不同的销售员类型,如只唱歌的销售员,只跳舞的销售员,或者同时会唱、跳、rap的销售员。
迪米特法则(Demeter Law, DML)提倡的是:一个对象 shouldn't expose its internal details to other objects. 即一个类不应该直接暴露其内部属性或方法。类与类之间应该保持尽可能低的耦合度。
在4S店的例子中,老板希望查看销售报表。我们可以设计一个Boss类,直接持有Seller和Report对象,并调用report方法。然而,这种设计会导致Boss类对Report类的实现产生直接依赖,这种耦合度很高,不易于维护。
为了遵循迪米特法则,我们可以重新设计Boss类,使其只与Seller类有关,而Seller类再与Report类交互。具体来说,Boss类可以调用seller.apply()方法,而Seller类持有Report对象,并在需要时调用report方法。这样,Boss类与Report类之间隔离了耦合度。
接口隔离原则(Interface Segregation Principle, ISP)指出:不应该存在一个接口,一个类依赖于它不需要的接口。类与类之间的依赖应该建立在最小的接口上。
在4S店的例子中,销售员需要卖车,同时还需要具备唱歌、跳舞、说rap等技能。我们可以设计一个总接口Skills,包含所有可能的技能方法(如sing()、jump()、rap()等)。然而,这样的设计会导致接口过于庞大,难以维护。
为了遵循接口隔离原则,我们可以将接口细化为多个独立的接口。例如,Sing、Jump、Rap等接口各自独立,销售员类只需要实现它需要的接口。这样,系统的设计更加灵活,维护性也更高。
里氏代换原则(Liskov Substitution Principle, LSP)指出:如果一个对象类型为S,另一个对象类型为T,且T是S的子类型,那么在T对象的所有行为中代替S对象时,程序的行为不会发生改变。
在4S店的例子中,老板认为新来的销售员只需要具备销售汽车的能力,就可以像资深销售员一样工作。我们可以设计一个NewSeller类继承自Salesman类,且在NewSeller类中实现Salesman类的所有方法。这样,当系统需要销售汽车时,可以直接使用NewSeller对象代替Salesman对象,从而确保行为的一致性。
此外,里氏代换原则还规定,子类可以有自己的个性定义。例如,NewSeller类可以在Salesman类的基础上增加一些额外的功能,如销售额统计、客户关系管理等。
合成/复用原则(Composition/Reuse Principle, CRP)强调在软件复用时,应该优先使用组合或聚合关系,而不是继承关系。只有在无法通过组合实现时,才使用继承关系。
在4S店的例子中,假设我们需要支持多种不同的销售场景,如线上销售、线下销售、促销销售等。我们可以通过引入一个SalesChannel接口,定义不同的销售场景方法(如online()、offline()、promotion()等)。然后,每个具体的销售场景类(如OnlineSales、OfflineSales、PromotionSales)都实现SalesChannel接口的相应方法。
这样一来,销售系统的结构更加灵活,新增销售场景只需要实现相应的接口,而不需要修改现有的代码。
以上七种设计原则是软件设计中必须遵循的基本原则。它们共同构成了软件设计的基础框架,帮助开发者构建出高效、可维护、灵活的软件系统。在实际开发中,我们需要根据具体的业务需求来选择和应用这些原则。同时,也要注意不要过分追求设计模式的使用,否则可能会增加系统的复杂性,降低开发效率。
如果你对这些设计原则有任何疑问,或者需要更深入的了解,欢迎在评论区留言,我会尽力解答!
转载地址:http://elvg.baihongyu.com/