鄂城网站建设,网站rar文件,自己做电视视频网站吗,网页版梦幻西游飞升攻略文章目录 一、面向对象五大原则1、单一功能#xff08;Single Responsibility Principle, SRP#xff09;2、开放封闭原则#xff08;Open/Closed Principle, OCP#xff09;3、里氏替换原则#xff08;Liskov Substitution Principle, LSP#xff09;4、接口隔离原则Single Responsibility Principle, SRP2、开放封闭原则Open/Closed Principle, OCP3、里氏替换原则Liskov Substitution Principle, LSP4、接口隔离原则Interface Segregation Principle, ISP5、依赖倒置原则Dependency Inversion Principle, DIP 前言 在软件开发领域面向对象编程OOP是一种重要的编程范式它通过封装、继承和多态等特性提高了代码的可重用性、灵活性和可维护性。C作为一种强大的面向对象编程语言充分体现了这些原则。在面向对象的设计中有五大核心原则被广泛认可和应用它们分别是单一职责原则SRP、开放封闭原则OCP、里氏替换原则LSP、接口隔离原则ISP和依赖倒置原则DIP。下面将逐一解析这五大原则在C中的应用。 一、面向对象五大原则
1、单一功能Single Responsibility Principle, SRP 一个类应该只有一个引起变化的原因即一个类只负责一项职责。这个原则强调类的专注性避免一个类承担过多的责任。当一个类承担多个职责时其内聚力会降低代码的可读性和可维护性也会受到影响。 2、开放封闭原则Open/Closed Principle, OCP 软件实体类、模块、函数等应该对扩展开放对修改封闭。这个原则鼓励通过继承和多态来实现功能的扩展而不是通过修改现有代码。这样可以在不改变原有代码的基础上添加新功能提高系统的灵活性和可维护性。 示例背景 假设有一个支付系统最初只支持信用卡支付。随着业务的发展需要添加对其他支付方式的支持如 PayPal 和比特币。为了遵循开放封闭原则可以设计一个抽象的支付接口然后为每种支付方式创建具体的实现类。这样当需要添加新的支付方式时只需要创建一个新的实现类并将其添加到系统中即可无需修改现有的代码。 #include iostream
#include string
#include vector// 抽象的支付接口
class IPayment {
public:virtual void pay(double amount) 0;virtual ~IPayment() {}
};// 信用卡支付的具体实现
class CreditCardPayment : public IPayment {
public:void pay(double amount) override {std::cout Paying amount using Credit Card. std::endl;}
};// PayPal支付的具体实现
class PayPalPayment : public IPayment {
public:void pay(double amount) override {std::cout Paying amount using PayPal. std::endl;}
};// 比特币支付的具体实现
class BitcoinPayment : public IPayment {
public:void pay(double amount) override {std::cout Paying amount using Bitcoin. std::endl;}
};// 支付处理类
class PaymentProcessor {
private:std::vectorIPayment* payments;
public:void addPaymentMethod(IPayment* payment) {payments.push_back(payment);}void processPayments(double amount) {for (IPayment* payment : payments) {payment-pay(amount);}}~PaymentProcessor() {for (IPayment* payment : payments) {delete payment;}}
};int main() {// 创建支付处理器PaymentProcessor processor;// 添加不同的支付方式processor.addPaymentMethod(new CreditCardPayment());processor.addPaymentMethod(new PayPalPayment());processor.addPaymentMethod(new BitcoinPayment());// 处理支付processor.processPayments(100.0); // 假设支付金额为100return 0;
}
3、里氏替换原则Liskov Substitution Principle, LSP 子类型必须能够替换掉它们的基类型。这个原则强调继承关系中的一致性。如果一个派生类不能替代其基类而不改变程序的正确性那么这个继承关系就是不合理的。 示例背景 下面给出一个违反里氏替换原则的示例假设有一个基类 Bird 和一个派生类 Penguin如下 #include iostream
using namespace std;class Bird {
public:virtual void fly() {cout I can fly! endl;}
};class Penguin : public Bird {
public:void fly() override {cout I cannot fly! endl;}
}; 在这个例子中Bird 类有一个 fly 方法该方法输出 I can fly!。Penguin 类继承自 Bird 并重写了 fly 方法输出 I cannot fly!。 在这个例子中Penguin 类违背了里氏替换原则因为它改变了基类 Bird 的 fly 方法的行为。根据里氏替换原则子类对象应该能够替换父类对象而不改变程序的正确行为。为了避免这种情况应该确保子类在重写父类的方法时不会改变其原有的行为契约。 4、接口隔离原则Interface Segregation Principle, ISP 不应该强迫客户依赖于它们不使用的方法。这个原则强调接口的粒度。一个接口应该只包含客户需要的方法避免接口过于庞大和复杂。 示例背景 示例中 IShape 接口包含了三个方法draw、getArea 和 getPerimeter。但是如果有一个只关心形状面积的客户它不需要实现 draw 和 getPerimeter 方法。为了遵循 ISP可以将接口拆分为更小的接口。 class IShape {
public:virtual void draw() const 0;virtual int getArea() const 0;virtual int getPerimeter() const 0;
};class Circle : public IShape {
public:void draw() const override { /* ... */ }int getArea() const override { /* ... */ }int getPerimeter() const override { /* ... */ }
};5、依赖倒置原则Dependency Inversion Principle, DIP 高层模块不应该依赖于低层模块二者都应该依赖于抽象抽象不应该依赖于细节细节应该依赖于抽象。这个原则强调通过抽象来解耦模块之间的依赖关系。高层模块应该依赖于抽象接口而不是具体的实现类。这样可以提高系统的灵活性和可扩展性。 示例背景 假设现在要做一个电商系统需要实现的基本功能是订单入库。 版本一违反依赖倒置原则 假设系统设计初期用的是SQL Server数据库。通常会定义一个SqlServer类用于数据库的读写。然后定义一个Order类负责订单的逻辑处理。由于订单要入库需要依赖于数据库的操作。因此在Order类中需要定义SqlServer类的变量并初始化。 // 定义SqlServer类负责与数据库进行交互
class SqlServer {
public:void add() {cout往数据库添加一个订单.endl;}
};// 定义Order类处理业务并使用SqlServer类提供的能力实现订单入库的功能
class Order {
private:SqlServer *p;
public:Order() {p new SqlServer;}void add() {// 先进行订单的逻辑处理再把这个订单放到数据库p-add();}
};如果要使用Oracle数据库那么要重新写一个OracleServer类然后对Order类进行修改程序扩展性比较差。上面程序扩展性不强的原因主要有下面两个 Order直接依赖于一个具体的类。Order依赖的对象的创建与绑定是在它的内部实现的。 下面的示例重点分析了下如何解决这两个问题 版本二符合依赖倒置原则 为了解决Order直接依赖于一个具体的类的问题可以定义一个抽象类DataAccess类DataAccess提供了操作数据库的接口Order类依赖抽象类DataAccess如下 class DataAccess {
public:virtual void add() {}
} ;class SqlServer : public DataAccess {
public:void add() {cout往 SQL 数据库添加一个订单.endl;}
};class Oracle : public DataAccess {
public:void add() {cout往 Oracle 数据库添加一个订单.endl;}
};class Order {
private:DataAccess re;
public:Order(DataAccess re):re(re) {}void add() {// 先进行订单的逻辑处理再把这个订单放到数据库re.add();}
};通过控制反转Inversion of Control缩写为IoC可以解决前面的第二个问题下面先介绍下什么是控制反转以及如何实现控制反转。 控制反转
定义 控制反转是一种设计思想它将对象的控制权从代码本身转移到外部容器或框架中。具体来说在采用控制反转之前对象通常会自己负责创建并管理它所依赖的其他对象。而在控制反转中对象的依赖关系会在其创建时或运行时由外部实体如IoC容器注入。实现方式 控制反转最常见的实现方式是依赖注入Dependency Injection简称DI。依赖注入允许在运行时动态地将依赖关系注入到对象中从而降低了对象之间的耦合度。依赖注入有多种实现形式包括 构造器注入 通过构造器将依赖对象传递给被依赖的对象。Setter方法注入 通过Setter方法将依赖对象设置到被依赖的对象中。接口注入 通过接口将依赖对象注入到被依赖的对象中。 可以通过构造函数将Order依赖的数据库对象注入给它如下 class Order{
private:DataAccess re;
public:// 通过构造函数接受依赖的数据库对象Order(DataAccess re):re(re) {}void add() {// 先进行订单的逻辑处理再把这个订单放到数据库re.add();}
};int main() {SqlServer sql; // 在外部创建依赖对象Order order1(sql); // 通过构造函数注入依赖order1.add();Oracle oracle; // 在外部创建依赖对象Order order2(oracle); // 通过构造函数注入依赖order2.add();return 0;
}让Order依赖抽象类DataAccess以及通过构造函数来注入Order依赖的数据库对象完美的解决了前面的示例存在的问题极大的提升了程序的可扩展性。