⼀个类应该仅有⼀个引起它变化的原因。
解释: 一个类或函数只承担一个职责。
比如一个用户类有一个修改账号和密码的成员函数,那这个成员函数就承担了两个职责:修改账号和修改密码,它们之间相互耦合。用户如果只想修改密码或者只想修改账号就得调用同一个函数,给调用增加了麻烦。如果后续增加修改邮箱的功能,是不是还得给这个函数增加修改邮箱的功能?
class User1 {
public:
//修改账号和密码
bool ModifyUserInformation(string information, int op){
//如果op为1,则修改账号
if (op == 1){
}
//op为2,修改密码
else if (op == 2){
}
//如果后续还要增加修改邮箱的功能,还需要传入op==3,这不仅不好维护
//而且也不方便程序员调用这个函数,因为并不知道op填多少
}
private:
//用户的账号
string _user_name;
//用户的密码
string _user_password;
//用户的邮箱
string _user_email;
};
class User2 {
public:
//修改账号
bool ModifyUserName(string new_name){
}
//修改账号
bool ModifyUserPassword(string new_password) {
}
//如果后续增加修改邮箱的功能,只需要再加一个成员函数即可
private:
//用户的账号
string _user_name;
//用户的密码
string _user_password;
//用户的邮箱
string _user_email;
};
再举一个例子,比如一个抽象类“家务”有两个函数“洗碗”和“拖地”,另外两个用户类,一个类只负责“洗碗”,一个类只负责“拖地”。
class HouseWork {
public:
virtual void WashDishes() = 0;
virtual void Mopping() = 0;
};
class User1 : public HouseWork {
public:
void WashDishes(){
cout << "用户1负责洗碗" << endl;
}
//因为父类是抽象类,所以User1必须要实现这个拖地的接口,即使它不负责拖地
void Mopping() {};
};
class User2 : public HouseWork {
public:
void WashDishes() {};
void Mopping() {
cout << "用户2负责拖地" << endl;
}
};
可以看到上面抽象类HouseWork有两个功能,User1和User2只需要其中一个功能时,还必须要实现另一个功能。并且如果HouseWork增加一个函数,比如做饭。那User1和User2就必须要再实现做饭这个函数,这是非常麻烦的。
因此,我们可以把这些功能分离出来单独实现成一个类:
class HouseWork {
};
//洗碗和拖地分离
class Wash:public HouseWork {
public:
virtual void WashDishes() = 0;
};
class Mop :public HouseWork{
public:
virtual void Mopping() = 0;
};
//如果想增加做饭的接口
class Cook :public HouseWork {
public:
virtual void Cooking() = 0;
};
class User1 : public Wash {
public:
void WashDishes() {
cout << "用户1负责洗碗" << endl;
}
};
class User2 : public Mop {
public:
void Mopping() {
cout << "用户2负责拖地" << endl;
}
};
//User3负责拖地和做饭
class User3 : public Mop,Cook {
public:
void Mopping() {
cout << "用户3负责拖地" << endl;
}
void Cooking() {
cout << "用户3负责做饭" << endl;
}
};
子类在继承类时,必须重写父类中所有的抽象函数。子类可以实现自己的函数,但不能覆盖掉父类的非抽象函数。
子类的对象能够替换其基类的对象被使用。
解释: 前面很好理解,在继承时经常用:
#include
//鸟
class Bird {
public:
Bird() {}
virtual ~Bird() {}
virtual void Fly() {
std::cout << "I am a bird and I am flying" << std::endl;
}
};
//燕子
class Swallow : public Bird {
public:
Swallow() {}
~Swallow() {}
void Fly() override {
std::cout << "I am a Swallow and I am flying" << std::endl;
}
};
//大雁
class WildGoose : public Bird {
public:
WildGoose() {}
~WildGoose() {}
void Fly() override {
std::cout << "I am a Wild Goose and I am flying" << std::endl;
}
};
//模拟鸟的飞行
void Fly(Bird& b) {
b.Fly();
}
int main() {
WildGoose goose;
Swallow s;
Fly(s);
Fly(goose);
}
子类的对象能够替换其基类的对象被使用。比如:任何使用Animal抽象类的地方,我们都可以用其子类如Cat类替代,因为Cat类重写了Animal的各种抽象函数。
模块间的依赖通过抽象发生,实现类之间不直接发生依赖关系,其依赖关系是通过接口或抽象类产生的;抽象不应该依赖具体实现,具体实现应该依赖于抽象类。
解释: 直接通过例子来说明,现在有两个类,披萨类和用户类,用户可以执行吃披萨的动作:
class Pizza {
public:
void eated() {
cout << "披萨正在被吃" << endl;
}
};
class User {
public:
void eat(Pizza food) {
cout << "用户正在吃饭" << endl;
food.eated();
}
//这样实现eat有很大的问题,比如后续如果增加吃汉堡的功能
//那还需要再实现一个eat函数
};
可以发现如果后续用户想要增加吃汉堡的功能,就得再写一个eat函数。为了解决这个问题,我们可以将它们之间的依赖关系抽象出来:
//使用抽象类解决了Food和User的依赖关系
class Food
{
public:
virtual void eated() = 0;
};
class IUser
{
public:
virtual void eat(Food *) = 0;
};
class Pizza : public Food
{
public:
void eated()
{
cout << "披萨正在被吃" << endl;
}
};
class Hamburger : public Food
{
public:
void eated()
{
cout << "汉堡正在被吃" << endl;
}
};
class User1 : public IUser
{
public:
void eat(Food *food)
{
cout << "用户正在吃饭" << endl;
food->eated();
//后续如果想增加吃其他食物的功能,比如吃饺子
//直接实现一个饺子类即可,不需要再写一个eat函数
}
};
int main()
{
User1 u1;
u1.eat(new Pizza);
u1.eat(new Hamburger);
}
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
这和“单一职责原则”类似,但它们还有一些不同:
单一职责原则:一个类或函数只承担一个职责,所以同一个功能的所有实现函数可能会放到同一个类中。
接口隔离原则:减少接口的粒度,每个类只需要实现自己需要的接口即可。
所以单一职责原则和接口隔离原则可能会无法同时保证。
尽量减少对象之间的交互,从而减小类之间的耦合。
当增加新功能,不应该通过修改已经存在的代码来进行,而是应该通过扩展代码,比如增加新类、增加新的成员函数 来实现。
正在完善,后面再写。。。。
C++设计模式----工厂模式
C++设计模式—原型模式
C++设计模式—建造者/构建器/构建者/生成器模式