网站卡的原因,网络艺术设计是什么,配件查询网站制作,网站制作寻找客户面试能说出这几种常用的设计模式即可
1.策略模式
1.1 业务场景
大数据系统把文件推送过来#xff0c;根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码#xff1a;
if(typeA){//按照A格式解析
}else if(typeB){//按照B格式解析
… 面试能说出这几种常用的设计模式即可
1.策略模式
1.1 业务场景
大数据系统把文件推送过来根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码
if(typeA){//按照A格式解析
}else if(typeB){//按照B格式解析
}else{//按照默认格式解析
}存在问题
如果分支变多这里的代码就会变得臃肿难以维护可读性低。如果你需要接入一种新的解析类型那只能在原有代码上修改。
以上代码违背了面向对象编程的开闭原则以及单一原则。
开闭原则对于扩展是开放的但是对于修改是封闭的增加或者删除某个逻辑都需要修改到原来代码单一原则规定一个类应该只有一个发生变化的原因修改任何类型的分支逻辑代码都需要改动当前类的代码。
如果你的代码有多个if…else等条件分支并且每个条件分支可以封装起来替换的我们就可以使用策略模式来优化。
1.2 策略模式定义
策略模式定义了算法族分别封装起来让它们之间可以相互替换此模式让算法的变化独立于使用算法的的客户。 大白话 假设你跟不同性格类型的小姐姐约会要用不同的策略有的请电影比较好有的则去吃小吃效果不错有的去逛街买买买最合适。当然目的都是为了得到小姐姐的芳心请看电影、吃小吃、逛街就是不同的策略。 策略模式针对一组算法将每一个算法封装到具有共同接口的独立的类中从而使得它们可以相互替换。
1.3 策略模式使用
context用一个ConcreteStrategy来配置维护一个对Strategy对象的引用。 策略模式和工厂模式的不同之处 工厂来生成算法对象这没有错但算法只是一种策略最重要的是这些算法是随时间都可能互相替换的这就是变化点而封装变化就是面向对象的一个重要的思维方式
1.4代码示例
#pragma once
#include iostream
using namespace std;class Strategy{public:virtual void algorithmInterface() 0;virtual ~Strategy(){cout ~Strategy() called! endl;}};class ConcreteStrategyA: public Strategy{public:void algorithmInterface(){cout arithmetic A is called! endl;}~ConcreteStrategyA(){cout ~ConcreteStrategyA() called! endl;}
};class ConcreteStrategyB: public Strategy{public:void algorithmInterface(){cout arithmetic A is called! endl;}~ConcreteStrategyB(){cout ~ConcreteStrategyB() called! endl;}
};class ConcreteStrategyC: public Strategy{public:void algorithmInterface(){cout arithmetic C is called! endl;}~ConcreteStrategyC(){cout ~ConcreteStrategyC() called! endl;}
};class Context{public:Context(Strategy *strategy){m_strategy strategy;}~Context(){delete m_strategy;}void contextInterface(){m_strategy-algorithmInterface();}private:Strategy *m_strategy;
};#include strategy.h
int main(){Context *contextA, *contextB, *contextC;//由于实例化不同的策略所以最终在调用context-contextInterface()时所获得的结果就不尽相同contextA new Context(new ConcreteStrategyA);contextA-contextInterface();delete contextA;cout endl ----------**********---------- endl;contextB new Context(new ConcreteStrategyB);contextB-contextInterface();delete contextB;cout endl ----------**********---------- endl;contextC new Context(new ConcreteStrategyC);contextC-contextInterface();delete contextC;getchar();return 0;
}2. 责任链模式
2.1 业务场景
请假是我们日常生活中经常遇到的事一般请假按请的时间长短需要跟不同级别的管理者请就是请假这个请求根据时间长短可由不同的处理者处理非常适合责任链模式。
场景不同的请求需要不同权限的对象来处理的情况。
2.2 模式结构 2.3 责任链模式定义
当你想要让一个以上的对象有机会能够处理某个请求的时候就使用责任链模式。 责任链模式为请求创建了一个接收者对象的链。执行链上有多个对象节点每个对象节点都有机会条件匹配处理请求事务如果某个对象节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。这种模式给予请求的类型对请求的发送者和接收者进行解耦。 责任链模式实际上是一种处理请求的模式它让多个处理器对象节点都有机会处理该请求直到其中某个处理成功为止。责任链模式把多个处理器串成链然后让请求在链上传递
大白话 假设你晚上去上选修课坐到了最后一排。来到教室发现前面坐了好几个漂亮的小姐姐于是你找张纸条写上“你好, 可以做我的女朋友吗如果不愿意请向前传”。纸条就一个接一个的传上去了后来传到第一排的那个妹子手上。
2.4代码示例
#include iostream
using namespace std;//抽象处理者
class Handler{
public:Handler() { m_pNextHandler NULL; }virtual ~Handler() {}//设置下一个处理者void SetNextHandler(Handler *successor) { m_pNextHandler successor; }//处理请求virtual void HandleRequest(int days) 0;
protected:Handler *m_pNextHandler; // 后继者
};//具体处理者、主管
class Director :public Handler{
public://处理请求virtual void HandleRequest(int days){if (days 1){cout 我是主管有权批准一天假同意了 endl; }else{m_pNextHandler-HandleRequest(days);}}
};//具体处理者、经理
class Manager :public Handler{
public://处理请求virtual void HandleRequest(int days){if (days 3){cout 我是经理有权批准三以下的假同意了 endl;}else{m_pNextHandler-HandleRequest(days);}}
};//具体处理者、老板
class Boss :public Handler{
public://处理请求virtual void HandleRequest(int days){if (days 7){cout 我是老板最多让你请7天假同意了 endl;}else{cout 你请的假事假太长了不同意 endl;}}
};//场景
int main(){Handler *director new Director;Handler *manager new Manager;Handler *boss new Boss;//设置责任链director-SetNextHandler(manager);manager-SetNextHandler(boss);director-HandleRequest(1);director-HandleRequest(2);director-HandleRequest(5);director-HandleRequest(8);return 0;
}/*输出
我是主管有权批准一天假同意了
我是经理有权批准三以下的假同意了
我是老板最多让你请7天假同意了
你请的假事假太长了不同意*/2.5优缺点
优点
单一职责原则。 你可对发起操作和执行操作的类进行解耦。 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。
缺点
每个请求都从头到尾遍历。 如果建链不当可能会造成循环调用这将导致系统陷入死循环
3.模板方法模式
3.1 适合场景
假设有这么一个业务场景内部系统不同商户调用我们系统接口去跟外部第三方系统交互http方式。走类似这么一个流程如下 一个请求都会经历这几个流程
查询商户信息对请求报文加签发送http请求出去对返回的报文验签
这里有的商户可能是走代理出去的有的是走直连。
3.2 定义
定义一个操作中的算法的骨架流程而将一些步骤延迟到子类中使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。它的核心思想就是定义一个操作的一系列步骤对于某些暂时确定不下来的步骤就留给子类去实现这样不同的子类就可以定义出不同的步骤。 模板方法模式是通过把不变行为搬移到超类中去除子类中的重复代码来提现他的优势。 模板方法模式提供了一个很好的代码复用。 当不变和可变的行为在方法的子类实现中混合在一起的时候不变的行为就会在子类中重复出现。通过模板方法模式把这些行为搬移到单一的地方这样就帮助子类摆脱重复不变行为的纠缠。
3.3 代码示例
#ifndef HEAD_H
#define HEAD_Hclass Fundamental{
public:virtual void primitiveOperation1();virtual void primitiveOperation2();void templateMethod();virtual ~Fundamental();
};class ConcreteClassA: public Fundamental{
public:void primitiveOperation1();void primitiveOperation2();virtual ~ConcreteClassA();
};class ConcreteClassB: public Fundamental{
public:void primitiveOperation1();void primitiveOperation2();virtual ~ConcreteClassB();
};#endif //HEAD_H#include Head.h
#include iostream
#include string
using namespace std;void Fundamental::primitiveOperation1(){cout Fundamental::primitiveOperation1() endl;
}void Fundamental::primitiveOperation2(){cout Fundamental::primitiveOperation1() endl;
}void Fundamental::templateMethod(){this-primitiveOperation1();this-primitiveOperation2();
}Fundamental::~Fundamental(){cout Fundamental::~Fundamental() endl;
}void ConcreteClassA::primitiveOperation1(){cout ConcreteClassA::primitiveOperation1() endl;
}void ConcreteClassA::primitiveOperation2(){cout ConcreteClassA::primitiveOperation1() endl;
}ConcreteClassA::~ConcreteClassA(){cout ConcreteClassA::~ConcreteClassA() endl;
}void ConcreteClassB::primitiveOperation1(){cout ConcreteClassB::primitiveOperation1() endl;
}void ConcreteClassB::primitiveOperation2(){cout ConcreteClassB::primitiveOperation2() endl;
}ConcreteClassB::~ConcreteClassB(){cout ConcreteClassB::~ConcreteClassB() endl;
}#include Head.h
#include iostream
#include string
using namespace std;int main(){ Fundamental *fundamental new ConcreteClassA;fundamental-templateMethod();delete fundamental;cout -------------- 华丽的分割线 -------------- endl;Fundamental *concrete new ConcreteClassB;concrete-templateMethod();delete concrete;getchar();return 0;
}4. 观察者模式
4.1 适合场景
登陆注册应该是最常见的业务场景了。就拿注册来说事我们经常会遇到类似的场景就是用户注册成功后我们给用户发一条消息又或者发个邮件等等因此经常有如下的代码
void register(User user){insertRegisterUseruser;sendIMMessage();sendEmail()
}这块代码会有什么问题呢如果产品又加需求现在注册成功的用户再给用户发一条短信通知。于是你又得改register方法的代码了。。。这是不是违反了开闭原则啦。
void register(User user){insertRegisterUseruser;sendIMMessage();sendMobileMessage;sendEmail()
}并且如果调发短信的接口失败了是不是又影响到用户注册了这时候是不是得加个异步方法给通知消息才好。可以使用观察者模式优化。 其它场景 1服务器发布新版本让客户端更新。 2游戏群发邮件给补贴等。 3聊天室系统。
4.2 观察者模式定义
观察者模式又叫做发布-订阅模式 观察者模式定义了一种一对多的依赖关系让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时会通知所有观察者对象使他们能够自动的更新自己。 观察者模式属于行为模式一个对象被观察者的状态发生改变所有的依赖对象观察者对象都将得到通知进行广播通知。它的主要成员就是观察者和被观察者。
被观察者Observerable目标对象状态发生变化时将通知所有的观察者。观察者observer接受被观察者的状态变化通知执行预先定义的业务。
使用场景 完成某件事情后异步通知场景。如登陆成功
4.3代码示例
#ifndef HEAD_H
#define HEAD_H#include iostream
#include string
#include list
using namespace std;class Observer;class Subject{
public:void attach(Observer *observer);void detach(Observer *observer);void notify();~Subject();private:listObserver* observers;
};class Observer{
public:virtual void update(){}virtual string getName(){ return ; }
};class ConcreteSubject : public Subject{
public:string getSubjectState();void setSubjectState(const string str);private:string subjectState;
};class ConcreteObserver : public Observer{
public:ConcreteObserver(ConcreteSubject *subject, string name);void update();string getName();private:string name;string observerState;ConcreteSubject *subject;
};#endif //HEAD_H#include Head.hvoid Subject::attach(Observer *observer){observers.push_back(observer);
}void Subject::detach(Observer *observer){observers.remove(observer);
}void Subject::notify(){listObserver*::iterator it observers.begin();while(it ! observers.end()){(*it)-update();it;}
}Subject::~Subject(){cout 开始析构了 endl;listObserver*::iterator it observers.begin();while(it ! observers.end()){cout 开始删除: (*it)-getName() endl;delete *it;it;}observers.clear();
}std::string ConcreteSubject::getSubjectState(){return subjectState;
}void ConcreteSubject::setSubjectState(const string str){subjectState str;
}ConcreteObserver::ConcreteObserver(ConcreteSubject *subject, string name){this-subject subject;this-name name;
}void ConcreteObserver::update(){observerState subject-getSubjectState();cout 发布者更新东西了 订阅者 name 状态 observerState endl;
}std::string ConcreteObserver::getName(){return name;
}#include Head.h
int main(int *argc, int *argv[]){ConcreteSubject *subject new ConcreteSubject;subject-attach(new ConcreteObserver(subject, 第一个粉丝));subject-attach(new ConcreteObserver(subject, 第二个粉丝));subject-attach(new ConcreteObserver(subject, 第三个粉丝));subject-setSubjectState(Hello);subject-notify();cout ----------- 华 丽 的 分 割 线 ----------- endl;subject-attach(new ConcreteObserver(subject, 王二麻子));subject-setSubjectState(呵呵);subject-notify();cout endl;delete subject;getchar();return 0;
}5. 工厂模式
5.1 业务场景
工厂模式一般配合策略模式一起使用。用来去优化大量的if…else…或switch…case…条件语句。
在程序中需要创建的对象很多导致对象的new操作多且杂时需要使用简单工厂模式 由于对象的创建过程是我们不需要去关心的而我们注重的是对象的实际操作所以我们需要分离对象的创建和操作两部分如此方便后期的程序扩展和维护。
5.2定义
简单工厂模式Simple Factory Pattern专门定义一个类来负责创建其他类的实例被创建的实例通常具有共同的父类。 是一种实例化对象的方式只要输入需要实例化对象的名字就可以通过工厂对象的相应工厂函数来制造你需要的对象。
5.3优缺点
优点
本着高内聚低耦合的原则将系统的逻辑部分和功能分开。
缺点
简单工厂模式会增加系统类的个数在一定程度上增加了系统的复杂度和理解难度 系统扩展难一旦增加新产品就需要修改工厂逻辑不利于系统的扩展与维护简单工厂模式中所有产品的创建都是由同一个工厂创建工厂类职责较重业务逻辑较为复杂具体产品与工厂类之间耦合度高严重影响了系统的灵活性和扩展性。
5.4 示例场景
某电视机厂为各个品牌代工生产电视机可以使用简单工厂的模式来实现。
5.5示例代码
#include iostream
#include vector
using namespace std;typedef enum ProductTypeTag{Hair,Hisense,
}PRODUCTTYPE;//抽象产品类 TV(电视机类)
class TV{
public:virtual void Show() 0;virtual ~TV(){};//声明析构函数为虚函数防止内存泄漏
};//具体产品类 HairTV(海尔电视类)
class HairTV : public TV{
public:void Show(){coutIm HairTV endl;}
};//具体产品类 HisenseTV(海信电视类)
class HisenseTV : public TV{
public:void Show(){coutIm HisenseTVendl;}
};// 工厂类 TVFactory(电视机工厂类)
class TVFactory{
public:TV* CreateTV(PRODUCTTYPE type){switch (type){case Hair:return new HairTV(); case Hisense:return new HisenseTV();default:return NULL;}}
};int main(int argc, char *argv[]){// 创建工厂类对象TVFactory* myTVFactory new TVFactory();TV* hairTV myTVFactory-CreateTV(Hair);if (hairTV ! NULL)hairTV-Show();TV* hisenseTV myTVFactory-CreateTV(Hisense);if (hisenseTV ! NULL)hisenseTV-Show();delete myTVFactory;myTVFactory NULL;delete hairTV;hairTV NULL;delete hisenseTV;hisenseTV NULL; return 0;
}6. 单例模式
6.1 业务场景
单例模式保证一个类仅有一个实例并提供一个访问它的全局访问点。I/O与数据库的连接,一般就用单例模式实现的。Windows里面的Task Manager任务管理器也是很典型的单例模式。
6.2定义
全局有且只有一个类的static实例在程序任何地方都能够调用到。比如游戏客户端的本地Excel的加载我们都会格式化成json。
一个好的单例应该具备下面4点
1.全局只有一个实例static 特性同时禁止用户自己声明并定义实例把构造函数设为 private2.线程安全3.禁止赋值和拷贝4.用户通过接口获取实例使用 static 类成员函数
6.3 单例模式的经典写法
6.2.1 懒汉模式
懒汉式(Lazy-Initialization)的方法是直到使用时才实例化对象也就说直到调用Instance() 方法的时候才 new 一个单例的对象 如果不被调用就不会占用内存。如果单线程没有问题当多线程的时候就会出现不可靠的情况。
#include iostream
using namespace std;/*
* 版本1 SingletonPattern_V1 存在以下两个问题
*
* 1. 线程不安全, 非线程安全版本
* 2. 内存泄露
*/
class SingletonPattern_V1{
private:SingletonPattern_V1() {cout constructor called! endl;}SingletonPattern_V1(SingletonPattern_V1) delete;SingletonPattern_V1 operator(const SingletonPattern_V1) delete;static SingletonPattern_V1* m_pInstance;public:~SingletonPattern_V1() {cout destructor called! endl;}//在这里实例化static SingletonPattern_V1* Instance() {if (!m_pInstance) {m_pInstance new SingletonPattern_V1();}return m_pInstance;}void use() const { cout in use endl; }
};//在类外初始化静态变量
SingletonPattern_V1* SingletonPattern_V1::m_pInstance nullptr;//函数入口
int main(){//测试SingletonPattern_V1* p1 SingletonPattern_V1::Instance();SingletonPattern_V1* p2 SingletonPattern_V1::Instance();return 0;
}存在的问题
获取了两次类的实例构造函数被调用一次表明只生成了唯一实例 1线程安全的问题当多线程获取单例时有可能引发竞态条件第一个线程在if中判断 m_pInstance是空的于是开始实例化单例;同时第2个线程也尝试获取单例这个时候判断m_pInstance还是空的于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来; 解决办法:加锁 2内存泄漏. 注意到类中只负责new出对象却没有负责delete对象因此只有构造函数被调用析构函数却没有被调用因此会导致内存泄漏。 **解决办法1**当然我们自己手动调用delete来进行释放是可以的但是维护在何处释放又成了问题。 解决办法2 使用共享指针;
** 线程安全、内存安全的懒汉式单例 **
#include iostream
using namespace std;
#include memory // C11 shared_ptr头文件
#include mutex // C11 mutex头文件
/*
* 版本2 SingletonPattern_V2 解决了V1中的问题
*
* 1. 通过加锁让线程安全了
* 2. 通过智能指针(shareptr 基于引用计数)内存没有泄露了
*/
class SingletonPattern_V2
{
public:~SingletonPattern_V2() {std::cout destructor called! std::endl;}SingletonPattern_V2(SingletonPattern_V2) delete;SingletonPattern_V2 operator(const SingletonPattern_V2) delete;//在这里实例化static std::shared_ptrSingletonPattern_V2 Instance() {//双重检查锁if (m_pInstance nullptr) {std::lock_guardstd::mutex lk(m_mutex);if (m_pInstance nullptr) {m_pInstance std::shared_ptrSingletonPattern_V2(new SingletonPattern_V2());}}return m_pInstance;}private:SingletonPattern_V2() {std::cout constructor called! std::endl;}static std::shared_ptrSingletonPattern_V2 m_pInstance;static std::mutex m_mutex;
};//在类外初始化静态变量
std::shared_ptrSingletonPattern_V2 SingletonPattern_V2::m_pInstance nullptr;
std::mutex SingletonPattern_V2::m_mutex;int main(){std::shared_ptrSingletonPattern_V2 p1 SingletonPattern_V2::Instance();std::shared_ptrSingletonPattern_V2 p2 SingletonPattern_V2::Instance();return 0;
}优缺点
优点
1基于 shared_ptr内部实现的是基于引用计数的智能指针每次实例被赋值或者拷贝都会引用1在内部的析构中判断引用计数为0的时候会调用真正的delete。用了C比较倡导的 RAII思想用对象管理资源当 shared_ptr 析构的时候new 出来的对象也会被 delete掉。以此避免内存泄漏。 2加了锁使用互斥锁来达到线程安全。这里使用了两个 if判断语句的技术称为双重检测锁好处是只有判断指针为空的时候才加锁避免每次调用 get_instance的方法都加锁锁的开销毕竟还是有点大的。
缺点
使用智能指针会要求外部调用也得使用智能指针就算用个typedef也是一长串代码不好维护且不美观。非必要不应该提出这种约束; 使用锁也有开销; 同时代码量也增多了实际上设计最简单的才是最好的。
最推荐的懒汉式单例(magic static)——局部静态变量-记这个
#include iostream
using namespace std;
/*
* 版本3 SingletonPattern_V3 使用局部静态变量 解决了V2中使用智能指针和锁的问题
*
* 1. 代码简洁 无智能指针调用
* 2. 也没有双重检查锁定模式的风险
*/
class SingletonPattern_V3{
public:~SingletonPattern_V3() {std::cout destructor called! std::endl;}SingletonPattern_V3(const SingletonPattern_V3) delete;SingletonPattern_V3 operator(const SingletonPattern_V3) delete;static SingletonPattern_V3 Instance() {static SingletonPattern_V3 m_pInstance;return m_pInstance;}
private:SingletonPattern_V3() {std::cout constructor called! std::endl;}
};int main(){SingletonPattern_V3 instance_1 SingletonPattern_V3::Instance();SingletonPattern_V3 instance_2 SingletonPattern_V3::Instance();return 0;
}这样保证了并发线程在获取静态局部变量的时候一定是初始化过的所以具有线程安全性。 C静态变量的生存期 是从声明到程序结束这也是一种懒汉式。
最推荐的一种单例实现方式
通过局部静态变量的特性保证了线程安全 (C11, GCC 4.3, VS2015支持该特性);不需要使用共享指针代码简洁不需要使用互斥锁。注意在使用的时候需要声明单例的引用 SingletonPattern_V3 才能获取对象
6.2.2 饿汉模式
比较饥饿、比较勤奋实例在初始化的时候就已经建好了不管你后面有没有用到都先新建好实例再说。这个就没有线程安全的问题但是呢浪费内存空间呀。
设计模式分类
创建型模式Creational Patterns
这些模式关注对象的创建过程以确保系统在创建对象时更灵活、更高效。
单例模式Singleton Pattern工厂方法模式Factory Method Pattern抽象工厂模式Abstract Factory Pattern建造者模式Builder Pattern原型模式Prototype Pattern
结构型模式Structural Patterns
这些模式关注对象组合的方式以实现更大的结构。
适配器模式Adapter Pattern装饰器模式Decorator Pattern代理模式Proxy Pattern外观模式Facade Pattern桥接模式Bridge Pattern组合模式Composite Pattern享元模式Flyweight Patter
行为型模式Behavioral Patterns
这些模式关注对象之间的通信、交互和分配职责。
策略模式Strategy Pattern模板方法模式Template Method Pattern观察者模式Observer Pattern发布订阅迭代器模式Iterator Pattern责任链模式Chain of Responsibility Pattern命令模式Command Pattern备忘录模式Memento Pattern状态模式State Pattern访问者模式Visitor Pattern中介者模式Mediator Pattern解释器模式Interpreter Pattern
设计模式原则
依赖倒置原则Dependency Inversion Principle
“要依赖于抽象不要依赖于具体针对接口编程不要针对实现编程 它强调了模块间的松耦合和可扩展性。该原则的核心思想是
高层模块不应该依赖于低层模块两者都应该依赖于抽象接口。抽象接口不应该依赖于具体实现细节具体实现细节应该依赖于抽象接口。 模块间的依赖关系应该通过抽象接口进行而不是直接依赖于具体的实现。这样可以实现模块之间的解耦使得系统更加灵活、可扩展和可维护。 通过针对接口编程我们可以定义抽象的接口高层模块只依赖于这些抽象接口而不需要关心具体的实现细节。这样在需要替换具体实现或引入新的实现时只需要保证实现接口的兼容性而不需要修改高层模块的代码。 这种方式也促进了代码的可测试性因为我们可以轻松地通过使用模拟对象或者依赖注入来进行单元测试而不需要依赖于具体的实现。 总之依赖倒置原则强调了面向抽象编程的重要性通过解耦和抽象提高系统的灵活性、可扩展性和可维护性。
开闭原则Open-Closed Principle
定义“软件实体类、模块、函数等应该对扩展开放对修改关闭。” 开闭原则强调了在设计和编写代码时应该通过扩展来增加功能而不是通过修改已有的代码来实现功能的变化。它促使我们设计出更加稳定、灵活、可扩展和可维护的软件系统。 开闭原则的核心思想是通过抽象和多态来实现可扩展性。具体来说可以通过以下方法来满足开闭原则
使用抽象来定义可扩展的接口或基类以便在不修改现有代码的情况下进行扩展。通过多态实现具体实现的替换以便在运行时选择不同的实现而不需要修改调用方的代码。使用策略模式、工厂模式等设计模式来实现开闭原则。 遵循开闭原则的好处包括
更好的可维护性通过扩展而不是修改已有代码减少了引入错误和破坏现有功能的风险。更高的可复用性通过抽象和多态可以将通用的功能封装为可重用的模块或组件。更好的可扩展性通过扩展接口或基类并实现新的具体实现可以灵活地增加新的功能。 开闭原则是面向对象设计中的一个重要指导原则它强调通过扩展而不是修改来实现功能变化以提高系统的可维护性、可复用性和可扩展性。
接口隔离原则Interface Segregation Principle
接口隔离原则的定义是“一个类对其他类的依赖应该建立在最小的接口上。” 接口隔离原则的目标是设计精简、高内聚、低耦合的接口避免不必要的接口依赖和接口膨胀以提高系统的灵活性和可维护性。 接口隔离原则的关键思想是将庞大的接口拆分为更小、更具体的接口以满足客户端的精确需求。这样客户端只需依赖于它们所需的接口而无需依赖于不相关或不需要的接口。 以下是遵循接口隔离原则的一些指导原则
定义细粒度的接口将大型接口拆分为更小的、更具体的接口以适应不同的客户端需求。接口应该精简而专注接口应该只包含客户端所需的方法不应该强迫客户端实现不需要的方法。避免胖接口和接口污染避免在接口中定义过多的方法以免给客户端带来不必要的负担和依赖。根据实际需求进行接口拆分根据具体的业务需求和使用场景灵活地拆分接口以满足客户端的需要。 遵循接口隔离原则的好处包括
减少接口间的依赖关系降低耦合度。提高系统的可维护性和可扩展性。提高代码的复用性和可测试性。更好地支持单一职责原则使接口和类的责任更加清晰。 总之接口隔离原则指导我们设计细粒度、专注、精简的接口避免接口膨胀和不必要的接口依赖以提高系统的灵活性和可维护性。
里氏替换原则Liskov Substitution Principle
它强调子类型必须能够替换其基类型而不会引发程序的错误或异常。 定义是“如果S是T的子类型那么在所有T类型的程序中对象可以被替换为S类型的对象而不会影响程序的正确性。” 换句话说子类对象应该能够在不破坏程序正确性的前提下替换其基类对象且程序的行为不会产生意外或错误的结果。 里氏替换原则要求子类必须遵循其基类的约束和契约。即子类的方法必须遵循基类的方法声明不能缩小基类方法的前置条件输入参数约束和扩大基类方法的后置条件输出结果约束。也就是说子类应该保持与基类相同的行为规范。 遵循里氏替换原则的好处包括
提高代码的可重用性和可扩展性通过子类对象的替换可以扩展系统的功能并保持代码的兼容性。降低代码的耦合性通过基于抽象的设计减少对具体实现的依赖提高代码的灵活性和可维护性。保证系统的稳定性子类的替换不会破坏系统的正确性提供了更可靠的程序行为。 然而应该注意的是里氏替换原则并不意味着子类完全不能修改或扩展基类的行为。子类可以通过方法重写和方法重载来添加新的功能或修改基类的行为但必须保持替换的一致性和不破坏原有约束。 总之里氏替换原则是一种指导设计和编写代码的原则通过保持子类对基类的替换能力提高系统的可重用性、可扩展性和稳定性。