《C++代码简洁之道》学习笔记:类的设计原则

一、让类尽可能小

庞大的、代码成百上千行的类通常难以理解,这种类的可维护性、可测试性、可复用性都很差。

作者建议一般类的代码不要超过50行。我认为很难做到。如果以50行为目标,那要创建很多小类。小类的确更容易使用、理解和测试,但要注意这些相互关联的类应该创建在一起,不要放到不同的文件里面,否则会使工程的文件数量急剧增多。

二、单一职责

一个类通常只有一个单一且明确的职责。遵循此原则的类通常非常清晰和容易理解。

单一职责是比代码行数更好的标准,没有违背此原则,不管代码有多少行都没问题。

三、开闭原则

类对于拓展应该是开放的,对于修改应该是封闭的。

由于用户需求变化等原因,类应该是可拓展的,但如果对已经过测试的功能进行大改那么后果是难以预料的。最好能在不修改现有代码的基础上进行拓展。

对于面向对象对象来说,这种方式是继承。通过继承,可以在不修改现有功能的基础上增加新功能。

四、里氏替换原则

当要因为拓展一个类而继承该类时,应该遵循:子类可以替换父类。即任何父类可以出现的地方,子类一定要可以出现。

具体来说应该遵循:

  • 子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
  • 子类可以增加自己特有的方法。
  • 子类实现父类的抽象方法时,方法的输入参数要比父类方法的输入参数更宽松。
  • 子类实现父类的抽象方法时,方法的返回值要比父类的返回值更严格。

五、接口隔离原则

这是编写抽象类的原则。即:接口应高度内聚、高度抽象。也就是说接口不应该包含实现类实现后无意义的成员函数。比如一个关于鸟的抽象类,不应该包含关于飞的成员函数,因为企鹅也是鸟但不能飞,企鹅类继承鸟的抽象类后实现飞的成员函数是没有意义的。

六、避免环依赖

如果A类的定义里面包含B类的成员变量,B类的定义里包含A类的成员变量,这就是环依赖,也是紧耦合的不良表现,编译无法通过。

处理办法有两种,一是使用指针:

#ifndef ACCOUNT_H
#define ACCOUNT_H

class Account
{
public:
    Account();
    void setOwner(class Customer *newOwner);

private:
    Customer * owner;
};

#endif // ACCOUNT_H
#ifndef CUSTOMER_H
#define CUSTOMER_H

class Customer
{
public:
    Customer();
    void setCustomerAccount(class Account *newCustomerAccount);

private:
     Account * customerAccount;
};

#endif // CUSTOMER_H
#include "account.h"
#include "customer.h"

int main(int argc, char *argv[])
{
    Account * account = new Account;
    Customer * customer = new Customer;
    account->setOwner(customer);
    customer->setCustomerAccount(account);
}

七、依赖倒置原则

抽象不应该依赖细节,细节应该依赖抽象。

上面处理环依赖的第二种方式就是:使A类继承一个接口A0,A类可以依赖B类,B类依赖接口A0而不是直接依赖A类。

#ifndef OWNER_H
#define OWNER_H

#include "string"
#include "memory"

class OWner
{
public:
    virtual ~OWner() = default;
    virtual std::string getName() = 0;
};

using OWnerPtr = std::shared_ptr<OWner>;

#endif // OWNER_H
#ifndef CUSTOMER_H
#define CUSTOMER_H

#include "owner.h"
#include "account.h"

class Customer : public OWner
{
public:
    Customer();
    void setCustomerAccount(AccountPtr newCustomerAccount);
    std::string getName();

private:
     AccountPtr customerAccount;
};

#endif // CUSTOMER_H
#ifndef ACCOUNT_H
#define ACCOUNT_H

#include "owner.h"

class Account
{
public:
    Account();
    void setOwner(OWnerPtr newOwner);

private:
    OWnerPtr owner;
};

using AccountPtr = std::shared_ptr<Account>;

#endif // ACCOUNT_H
#include "account.h"
#include "customer.h"

int main(int argc, char *argv[])
{
    Account * account = new Account;
    Customer * customer = new Customer;
    account->setOwner(std::shared_ptr<Customer>(customer));
    customer->setCustomerAccount(std::shared_ptr<Account>(account));
}

八、最少知识原则

一个对象应当对其他对象尽可能少的了解,不和陌生人说话。

即不希望类之间建立直接的联系(两个类不直接通信)。如果真的有需要建立联系,也希望能通过它的友元类来转达。

这可能有一个副作用,即有大量类的存在只是为了用作在类之间传达信息,增加了系统的复杂度。

class A
{
public:
    A() = default;
    void info()const
    {
        qDebug()<<"我是A,你呢?";
    }

    friend class C;
};

class B
{
public:
    B() = default;
    void info()const
    {
        qDebug()<<"我是B";
    }
    friend class C;
};

class C
{
public:
    C() = default;
    void introduce(const A & a,const B & b)const
    {
        a.info();
        b.info();
    }
};

int main(int argc, char *argv[])
{
    A a;
    B b;
    C c;
    c.introduce(a,b);
}

九、避免“贫血”类

“贫血”类是指类里面只包含私有成员和成员的 getter、setter 函数。这种数据类型完全不必封装成类,把数据包装成结构体用起来更方便。

十、只说不问原则

尽量少地获取其他对象的状态。

class doSomeThing
{
public:
    void start()
    {
        if(a.isRuning()
        {
            a.doSome();
        }
        if(b.isRuning()
        {
            b.doSome();
        }
        if(c.isRuning()
        {
            c.doSome();
        }
    }
private:
    A a;
    B b;
    C c;
};

根据只说不问原则,上面的代码可以改成:

class doSomeThing
{
public:
    void start()
    {
        a.doSome();
        b.doSome();
        c.doSome();
    }
private:
    A a;
    B b;
    C c;
};

不去获取成员变量的状态,把是否 Runing 等判断放到对象自身的函数中处理。这样做是为了增强类的内聚性。

十一、慎重使用类的静态成员

一般的工具类会大量使用甚至全部使用静态成员。

大量使用静态成员的缺点:

  1. 破坏了类的封装性。
  2. 会使类的内聚性很弱甚至没有。
  3. 工具类的代码量会十分庞大,这不符合类应该尽量小的原则。
  4. 工具类的功能十分多样化,这不符合类应该具有单一职责的原则。

大量使用静态成员是一种面向过程的编程方法,如果需要大量的工具方法,可以把它们声明在一个头文件里面但建议不要封装成类。

静态成员常量和创建对象实例的静态成员函数(工厂方法)不在此列。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值