C++面向对象设计原则

1.单一职责-SRP(Single-Resposibility Principle

        一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而大大损伤其内聚性和耦合度。通常意义下的单一职责,就是指只有一种单一功能,不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。

2.开闭原则-OCP(Open-Closed Principle)

        面向扩展开放:设计应该具备良好的扩展性,能够应对新需求的冲击。当需要扩展新功能时,不需要或者很少需要改动现有接口。

        面向修改关闭:部分资料存在对读者的误导,软件设计不应该是不允许修改,而是良好的设计不需要修改对外接口,这样就将修改范围关闭在当前模块内,不会波及其他模块。这也体现了软件设计对高内聚和低耦合的追求。

        下面以工厂模式进一步阐述,对外接口是面向扩展开放的,目前只支持创建MP3和MP4。如果有了新的需求,需要当前模块支持MP5,那么当前模块的内部文件需要实现CMP5,但是其他模块不需要修改,只要在调用工厂类的MakeObj接口时,传入strType 为“MP5”就可以了。这样就将修改范围关闭在了当前模块内部,其他模块不需要修改,甚至不需要重新编译。

//.h 接口文件
class IElec {
public:
    virtual ~IElec() {};
    virtual double_t GetPower() = 0;
};

class CFactory{
    IElec* MakeObj(const std::string& strType);
};
//.h 当前模块的具体实现,可以拆分到.cpp,但不需要对外暴露
class CMP3 :public IElec{
public:
    double_t GetPower() override
    {
        return 5.0;
    }
};

class CMP4 :public IElec{
public:
    double_t GetPower() override
    {
        return 10.0;
    }
};

IElec* CFactory::MakeObj(const std::string& strType)
{
    if ("MP3" == strType)
    {
        return new CMP3();
    }
    else if ("MP4" == strType)
    {
        return new CMP4();
    }
    else
    {
        return nullptr;
    }
}

3.里氏替换-LSP(Liskov Substitution Principle)

        派生类对象的引用或指针,可以通过基类的引用或指针来表达。该原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余,避免运行期的类型判别。

        例如2.示例中的工厂类MakeObj返回的IElec*,既可以表达CMP3* 又可以表达CMP4*。

4.依赖倒置-DIP(Dependecy-Inversion Principle)

        高层模块不应该依赖低层模块,它们应该依赖于抽象接口。同时,抽象不应该依赖于细节,细节应该依赖于抽象。这种设计原则可以提高代码的灵活性、可扩展性和可维护性。

        下面通过一个依赖注入示例,进一步阐述。假设CPowerBank(充电宝)能够根据连接的电子产品类型,自动调节电压m_dPower。当ConnectElec传入p的实际对象为CMP3时,那么m_dPower为5.0;而当传入p的实际对象为CMP4时,那么m_dPower为10.0。

        在此示例工作过程中,只需要依赖IElec这个高层模块,而不需要依赖CMP3或者CMP4这些低层模块。依赖抽象,而不依赖具体实现。

class CPowerBank {
public:
    void ConnectElec(const IElec* p)
    {
        m_p = p;
    }

    void AutoSetPower()
    {
        if (nullptr == m_p) return;
        m_dPower = m_p->GetPower();
    }
private:
    IElec* m_p;
    double_t m_dPower = 0.0;
};

5.接口隔离-ISP(Interface-Segregation Principle)

        使用多个小的专门的接口,而不要使用一个大的总接口。接口应该是内聚的,一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,造成接口污染。

        如果说依赖倒置已经实现了细节与抽象的隔离,那么接口隔离原则就是对所谓的抽象再限定一个范围,该范围针对高层模块的需求订制。仍然以上述示例进一步阐述,CPowerBank只关心调用的模块的电压,而不关心调用对象的形状/颜色等特征。如果有另一个类关心MP3/MP4形状颜色,那么CMP3/CMP4需要继承并实现对应的接口,但是IElec这个接口可以保持不变。这样就将电子产品的颜色/形状等隔离在了当前模块或有需求的模块中,不会影响到CPowerBank类。

6.迪米特法则-LOD(Law-Of-Demeter)

        也被称为‌最少知识原则(Least Knowledge Principle, LKP)。一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。一个类对于其它类知道的越少越好,只和朋友说话,不要和陌生人说话。上述接口隔离和依赖倒置也都体现了这一原则,但迪米特法则的覆盖面则更广。任何模块和模块之间存在的直接或间接的相互作用,都应该尽可能遵循迪米特法则。

        仍以上述示例进一步展开,假如电子产品的型号由名称/颜色/形状组合而成,例如MP3-RED-RECT,为红色矩形的MP3;MP4-BLUE-CIRCLE,为蓝色圆形的MP4。如果产品类实现了分别具体的名称/颜色/形状的接口,由高层模块去分别获取这些信息然后拼接出产品型号,虽然可以实现功能,但是不符合迪米特法则。正常的做法应该是由产品类开出一个接口,直接获取产品型号字符串。也就是说,高层模块没有必要知道产品型号的生成方式。

7.合成复用原则-CRP(Composite Reuse Principle)

        如果有人说面向对象六大原则,那么就是上述的1~6。如果说7大原则,那么就包含本条原则。

        合成复用原则也叫组合优于继承原则,要求尽量使用对象组合,而不是通过继承来达到复用的目的。因为使用继承的时候,基类的变化可能会影响到子类,而如果使用组合/聚合就可以降低这种依赖关系,让类具有更好的可维护性和扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞翔的小七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值