设计模式 - 七大基本原则

面向对象设计模式的七大基本原则:
一、单一职责原则(Single Responsibility Principle,SRP)
二、开闭原则(Open Closed Principle,OCP)
三、里氏替换原则(Liskov Substitution Principle,LSP)
四、依赖倒置原则(Dependency Inversion Principle,DIP)
五、接口隔离原则(Interface Segregation Principle,ISP)
六、迪米特法则(Law of Demeter,LoD)/最少知识原则(Least Knowledge Principle,LKP)
七、合成复用原则(Composite Reuse Principle,CRP)/组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)

1、单一职责原则

定义:指一个类或者模块应该有且只有一个改变的原因。可以简单的认为一个类或者方法只做一件事情。
优点:降低复杂度、降低耦合性、提高可读性、高内聚、提高系统可维护性。

如果一个类Class负责两项职责A、B,对A的修改会同时引起对B的修改,这样就违反了单一职责,降低了可维护性。
实际操作过程中,对于职责的划分并不固定,需要根据经验合理确定。

2、开闭原则

定义:对于类、模块、函数等可以拓展,但不可修改。
优点:提高代码可复用性、提高系统稳定性、可维护性。

开闭原则是OOP程序设计中最重要的原则,其它原则大多围绕开闭原则展开,开闭原则要求在设计代码过程中,更多的考虑代码的可扩展性,而减少后期迭代过程中对原有代码的修改。

比如对于工厂Factory,它有一个生产书的方法BookSpawn,可以生产文学类书籍LiteratureBook 和计算机类书籍ComputerBook

public enum BookType
{
    Literature,
    Computer
}
public interface IBook { }
public class LiteratureBook : IBook { }
public class ComputerBook : IBook { }
public class Factory
{
    public IBook BookSpawn(BookType type)
    {
        switch (type)
        {
            case BookType.Literature:
                return new LiteratureBook();
            case BookType.Computer:
                return new ComputerBook();
            default:
                return null;
        }
    }
}

这种写法如果后面再去生产经济类书籍EconomicBooks,显然就要去修改原有的BookSpawn方法,这就违反了开闭原则。
更合理的写法是将工厂抽象出来,分别由不同的书籍工厂进行生产,而后期添加新的书籍只需要扩展工厂,实现生产的方法就可以了,避免对原有代码的修改

public interface IBook { }
public class LiteratureBook : IBook { }
public class ComputerBook : IBook { }
public interface IFactory
{
    IBook BookSpawn();
}
public class LiteratureFactory : IFactory
{
    public IBook BookSpawn()
    {
        return new LiteratureBook();
    }
}
public class ComputerFactory : IFactory
{
    public IBook BookSpawn()
    {
        return new LiteratureBook();
    }
}

当然开闭原则的可扩展不可修改并不是说一定不能修改原有代码。当原有代码不能承担新的功能需求,对它的扩展变得更加复杂的时候就需要考虑修改原有的代码,不能死守着原则不放,原则是相对的并不是绝对的。

3、里氏替换原则

定义:子类可以扩展父类的功能,但是不能改变父类原有的功能。简单说就是子类可以添加新的方法,但尽量不去重写父类已实现的方法。
优点:提高代码可复用性

里氏替换原则是对继承的一种限制和约束,继承中多扩展、少修改,这是实现对开闭原则的重要手段之一。

假设ClassA有一个方法Calculate,ClassB继承ClassA

public class ClassA
{
    public virtual float Calculate(float a, float b)
    {
        return a * b;
    }
}
public class ClassB: ClassA
{
    public override float Calculate(float a, float b)
    {
        return a / b;
    }
}

父类ClassA中的方法计算的是两数相乘,而子类ClassB重写了Calculate方法计算两数相除,这时候调用子类中的方法就不能得到正确的结果,甚至程序出错。这种继承违反里氏替换原则,是没有意义的。

4、依赖倒置原则

定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
优点:降低耦合性、提高系统稳定性、提高可维护性、有利于并行开发。

依赖倒置原则的核心思想是:要面向接口编程,不要面向实现编程,这是实现开闭原则的重要手段之一。

举个例子,人类可以吃肉获得营养:

public class Meat
{
    public void Nutrition()
    {
    }
}
public class Person
{
    public void Eat(Meat meat)
    {
        meat.Nutrition();
    }
}

这时再添加一个人类吃蔬菜获得营养,就需要在Person类中添加吃蔬菜打方法:

public class Vegetables
{
    public void Nutrition()
    {
    }
}
public class Person
{
    public void Eat(Meat meat)
    {
        meat.Nutrition();
    }
    public void Eat(Vegetables vegetables)
    {
        vegetables.Nutrition();
    }
}

这样当再去添加人类吃水果时,又需要去修改Person类。这种不断修改原有类来实现新功能的做法是不符合开闭原则的,而且增加了类之间的耦合性,不利于代码的维护。
更合理的做法是将Meat、Vegetables等抽象为一个接口IFood,IFood提供营养Nutrition,Person只需要吃IFood就可以了,而IFood具体要提供什么Nutrition就可以在它的实现中编写,Person并不需要去关心,降低了与其他类的耦合。

public interface IFood
{
    void Nutrition();
}
public class Meat: IFood
{
    public void Nutrition()
    {
    }
}
public class Vegetables : IFood
{
    public void Nutrition()
    {
    }
}
public class Person
{
    public void Eat(IFood food)
    {
        food.Nutrition();
    }
}

这种面向接口编程的模式将编程工作很好的区分成两部分:高层 、低层。高层负责调用接口实现具体逻辑,低层负责实现接口展示具体功能,非常适合并行开发,是MVC框架中逻辑与UI分离的基础。

5、接口隔离原则

定义:为各个类建立它们需要的最小接口,而不是去建立一个庞大的接口供所有的类去调用。
优点:提高内聚、提升灵活性、减少代码冗余。

接口隔离原则是对接口的一种约束,它要求细化接口,减少接口中的方法,用最小的接口完成最多的事情。接口应该针对模块、某一功能或者某一类专门定制,建立最小的依赖关系。最小接口可以提高灵活度,但也需要有个限度,过于细化的接口反而造成设计复杂化,起到反作用。

6、迪米特法则/最少知识原则

定义:只与你的直接朋友交谈,不跟“陌生人”说话。含义就是一个软件实体应当尽可能少地与其他实体发生通讯。
优点:降低耦合度、提高可复用性、可扩展性。

迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。而其它没有直接关系的类相互调用可以通过创建中介类实现,但需要注意过多的中介类会增加系统的复杂度,反而带来负面影响。
因此在类的划分上,应该考虑创建弱耦合的类,耦合越弱越利于复用,并且对弱耦合类的修改不会产生太大的波及;在类的结构上,降低类中成员及方法的访问权限,只对外提供必需的访问权限。

7、合成复用原则/组合/聚合复用原则

定义:在代码复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
优点:维持原有类的封装性,“黑箱”复用,新旧类之间的耦合度低、复用灵活性强。

合成复用原则是通过将已有的对象作为新对象的成员纳入新对象中来实现的,新对象可以调用已有对象的功能,从而达到复用。
例如手机MobilePhone分为安卓手机、苹果手机,而每种手机又有白色手机、黑色手机之分,如果使用继承关系,那么它们的结构如下:

public class MobilePhone { }
public class AndroidPhone : MobilePhone { }
public class IOSPhone : MobilePhone { }
public class WhiteAndroidPhone : AndroidPhone { }
public class BlackAndroidPhone : AndroidPhone { }
public class WhiteIOSPhone : IOSPhone { }
public class BlackIOSPhone : IOSPhone { }

可以看到这种继承关系,在新旧类两两之间有很强的的耦合关系,新类可以获取到旧类所有的成员和方法。
这时候就可以使用组合将颜色作为手机的一个成员属性来降低颜色和操作系统之间的耦合:

public class PhoneColor { }
public class MobilePhone
{
    public PhoneColor phoneColor;
}
public class AndroidPhone : MobilePhone { }
public class IOSPhone : MobilePhone { }
总结

七种设计原则的中心思想就是为了实现高内聚、低耦合
开闭原则是总纲:对扩展开放,对修改关闭;
里氏替换原则:不要破坏继承体系;
依赖倒置原则:面向接口编程;
单一职责原则:类要职责单一;
接口隔离原则:接口设计时要精简单一;
迪米特法则:降低耦合度;
合成复用原则:优先使用组合或者聚合关系复用,少用继承关系复用。

这 7 种设计原则在软件设计过程中并不是单一的遵守或者不遵守,而是看遵守的程度,任何事都是过犹不及的,而我们需要做的就是将其控制在一个合理的范围内。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值