顾名思义,对于一个类来讲,一个类只负责一个职责(对于一个函数来讲,一个函数也尽可能的完成一个功能),如果类A负责两个职责分别是:职责1和职责2,那么当职责1的需求变动从而需要修改类A,可能会造成职责2执行错误,因此需要将类A的粒度分解为A1,A2.
单一职责的优点1)可以降低类的复杂程度 2)可以提高类的可读性和维护性
3)降低因为业务修改带来的风险
将臃肿庞大的接口拆分成更小和更具体的接口。要为各个类建立它们需要的专用接口,而不要试图去建立一个庞大的接口供所有一类它的类去调用。
// 原实现:
class Bird
{
public:
virtual ~Bird() = default;
virtual void fly() = 0;
virtual void eat() = 0;
virtual void run() = 0;
virtual void move() = 0;
};
class Sparrow : public Bird
{
public:
void fly() override {}
void eat() override {}
void run() override {}
void move() override {}
};
class Penguin : public Bird
{
public:
void fly() override
{
// 本来不需要这个接口,但是继承了fly(),还是要实现
}
void eat() override {}
void run() override {}
void move() override {}
};
//修改
class Lifeform
{
public:
virtual void eat() = 0;
virtual void move() = 0;
};
class Flyable
{
public:
virtual void fly() = 0;
};
class Audible
{
public:
virtual void makeSound() = 0;
};
class Sparrow : public Lifeform, public Flyable, public Audible
{
public:
//...
};
class Penguin : public Lifeform, public Audible
{
public:
//...
};
依赖倒置原则:依赖于抽象(接口),不依赖于具体的实现(类),也就是针对接口编程。
//示例
#include
#include
using namespace std;
class AbsBook
{
public:
virtual void story() = 0;
};
class Book : public AbsBook
{
public:
void story() override
{
// 实现
}
};
class Book1 : public AbsBook
{
public:
void story() override
{
// 实现
}
};
class Book2 : public AbsBook
{
public:
void story() override
{
// 实现
}
};
class Mother
{
public:
void story(AbsBook *book)
{
book->story();
}
};
int main()
{
Mother *mother = new Mother;
AbsBook *book = new Book1;
mother->story(book);
return 0;
}
我们让其他的具体类依附于AbsBook这个抽象类(接口),这样无论Book里面的方法怎么变化我们的Mother里的方法永远是不变的,将高层和底层的中间用一个抽象类隔开都依赖于中间的抽象抽象层,两个层次怎么改变都不会相互影响,体现了c++的多态性,更好的帮助我们管理代码。
定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
定义2:所有引用基类的地方必须能透明地使用其子类的对象。
总结:尽可能的不在子类中重写父类的方法,可以变更类之间的关系。
// 示例
class Bird
{
public:
void setspeed(double speed){bird_speed=speed;}
double gettime(double dis){return (dis/bird_speed);}
public:
double bird_speed;
};
class Swallow:public Bird
{
};
class BrownKiwi: public Bird
{
public:
void setspeed(double speed){bird_speed=0;} // 重写了父类的函数,可能会导致程序崩溃
};
开闭原则主要概括为:对扩展开放,对修改关闭。
增加功能是通过增加代码实现的,而不是修改源代码。
程序员更喜欢添加类来增加功能,而不是修改源代码。
修改源代码及其容易犯错,若项目文件过大时,类与类的关联性强,改的地方就会特别多。一个类最好只做一件事情。
// 原设计方式
class calculator
{
public:
calculator(int a, int b, string mop)
{
this->ma = a;
this->mb = b;
this->moperator = mop;
}
int getResult()
{
if (moperator.compare("+") == 0)
return ma + mb;
else if (moperator.compare("-") == 0)
return ma - mb;
else if (moperator.compare("*") == 0)
return ma * mb;
}
public:
int ma, mb;
string moperator;
};
// 优化的方式
class AbstractCalculator
{
public:
virtual int getResult() = 0;
virtual void setOperatorNumber(int a, int b) = 0;
};
//加法计算器类 一个类只做一件事
class PlusCalculator :public AbstractCalculator
{
public:
virtual void setOperatorNumber(int a, int b)
{
this->mA = a;
this->mB = b;
}
virtual int getResult()
{
return mA + mB;
}
public:
int mA;
int mB;
};
//减法计算器类
class MinuteCalculator :public AbstractCalculator
{
public:
virtual void setOperatorNumber(int a, int b)
{
this->mA = a;
this->mB = b;
}
virtual int getResult()
{
return mA - mB;
}
public:
int mA;
int mB;
};
//乘法计算器类
class MultiplyCalculator :public AbstractCalculator
{
public:
virtual void setOperatorNumber(int a, int b)
{
this->mA = a;
this->mB = b;
}
virtual int getResult()
{
return mA * mB;
}
public:
int mA;
int mB;
};
1) 一个对象应该对其他对象保持最少的了解
2) 类与类关系越密切,耦合度越大
3) 迪米特法则 ( Demeter Principle ) 又叫 最少知道原则 ,即一个类对自己依赖的类知道的 越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内 部。对外除了提供的public 方法,不对外泄露任何信息
4) 迪米特法则还有个更简单的定义:只与直接的朋友通信
5) 直接的朋友 :每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系, 我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合 等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而 出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量 的形式出现在类的内部
迪米特法则注意事项和细节
1) 迪米特法则的核心是 降低类之间的耦合
2) 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间 ( 对象间 ) 耦合关系, 并不是要求完全没有依赖关系
// 示例
class Star
{
public:
Star(std::string name)
{
this->name = name;
}
std::string getName()
{
return this->name;
}
private:
std::string name;
};
/**
* @version v1.0
* @ClassName: Fans
* Description: 粉丝类
* @Author: LN
*/
class Fans
{
public:
Fans(std::string name)
{
this->name = name;
}
std::string getName() {
return this->name;
}
private:
std::string name;
};
/**
* @version v1.0
* @ClassName: Company
* Description: 媒体公司类
* @Author: LN
*/
class Company
{
public:
Company(std::string name)
{
this->name = name;
}
std::string getName()
{
return this->name;
}
private:
std::string name;
};
/**
* @version v1.0
* @ClassName: Agent
* Description: 经纪人类
* @Author: LN
*/
class Agent
{
public:
Agent():star(""), fans(""), company(""){}
void setStar(Star star) {
this->star = star;
}
void setFans(Fans fans) {
this->fans = fans;
}
void setCompany(Company company) {
this->company = company;
}
//粉丝见面会
void meeting() {
std::cout << this->star.getName() << "明星和" << this->fans.getName() << "粉丝见面了" << std::endl;
}
//洽谈业务
void business() {
std::cout << this->company.getName() << "与明星" << this->star.getName() << "洽谈业务" << std::endl;
}
private:
Star star;//明星
Fans fans;//粉丝
Company company;//媒体公司
};
1) 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2) 针对接口编程,而不是针对实现编程。
3) 为了交互对象之间的松耦合设计而努力, 少用继承。
// 示例 比如当前有个类A,成员函数Func1(),现在想要增加个新功能Func2()
// 原 类
class A
{
public:
A();
void Func1();
private:
};
// method 1 不符面向对象的思想,对原类的修改比较大,不可取。
class A
{
public:
A();
void Func1();
void Func2();
private:
};
// method 2 当父类修改时,子类也跟着变动,耦合度比较高。有没有更好的方法呢?
class B : public A
{
public:
B();
void Func2();
private:
};
// method 3
class C
{
public:
C(A *a);
void setA(A *a)
{
mA = a;
}
void Func2();
private:
A *mA;
};
这个专栏之后的内容,将详细介绍在工作中常用的设计模式,同时还会补充说明UML概念,和表示方法,如果文中有什么问题,希望欢迎指正。