写点什么

【愚公系列】2022 年 06 月 面向对象设计原则 (六)- 合成复用原则

作者:愚公搬代码
  • 2022 年 6 月 11 日
  • 本文字数:1978 字

    阅读完需:约 6 分钟

前言

常用的面向对象设计原则有七个,这七大设计原则都是以可维护性和可复用性为基础的,这些原则并不是孤立存在的,它们相互依赖相互补充,遵循这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性。

一、合成复用原则(Composite Reuse Principle or CRP)

<font color=#999AAA >尽量使用对象组合,而不是继承来达到复用的目的。合成复用原则是鼓励优先使用对象的组合,而不是使用继承。

二、使用步骤

示例

public abstract class BankCard {
public string UserName { get; set; }//用户名 public string Balance { get; set; }//余额
public abstract void Transfer(BankCard card, double amount);//转账 public abstract void Withdraw(double amount);//取款 public abstract void Deposit(double amount);//存款 public abstract void Overdraft(double amount);//透支
public virtual void ShowMessage() { Console.WriteLine($"THis is {this.ToString()}!"); }
}
复制代码


//建设银行普通银行卡public class CCBCard : BankCard {
public override void Deposit(double amount) { Console.WriteLine("Deposit"); }
public override void Overdraft(double amount) { throw new NotImplementedException(); }
public override void Transfer(BankCard card, double amount) { Console.WriteLine("Transfer"); }
public override void Withdraw(double amount) { Console.WriteLine("Withdraw"); }
}
复制代码


以上这种设计将银行卡的大部分功能封装在抽象基类 BankCard 中,建设银行普通银行卡类 CCBCard 类继承这个基类,但是建行的普通银行卡显然没有透支功能,导致导致不得不为这个没有透支功能的银行卡添加一个默认的透支实现,这显然是不合理的。另外如果修改基类中任何一个功能,所有继承自 BankCard 的类可能都要重写,这显然也不符合开闭原则。转账方法使用 BankCard 类型的参数,显然不符合里氏替换原则。以下给出一个解决方案以供参考:


public abstract class BankCardBase {
public string UserName { get; set; }//用户名 public string Balance { get; set; }//余额
public virtual void ShowMessage() { Console.WriteLine($"THis is {this.ToString()}!"); }
}
复制代码


独立一个抽象基类 BankCardBase,提供用户名、余额等基本功能。


public interface ICard {
void Transfer(BankCardBase card, double amount);//转账 void Withdraw(double amount);//取款 void Deposit(double amount);//存款
}
public interface ICreditCard : ICard {
void Overdraft(double amount);//透支
}
复制代码


抽象出 ICard 银行卡基本操作接口,包含转账、取款和存款。ICreditCard 信用卡接口,考虑到信用卡一定包含普通银行卡功能,所以继承 ICard 接口。


//建设银行普通银行卡public class CCBCard : BankCardBase, ICard {
public void Deposit(double amount) { Console.WriteLine("Deposit"); }
public void Transfer(BankCardBase card, double amount) { Console.WriteLine("Transfer"); }
public void Withdraw(double amount) { Console.WriteLine("Withdraw"); }
}
复制代码


建设银行普通银行卡实体类,继承自银行卡抽象基类并实现 ICard 银行卡接口。


//建设银行龙卡信用卡public class CCBDragonCard : BankCardBase, ICreditCard {
public void Deposit(double amount) { Console.WriteLine("Deposit"); }
public void Overdraft(double amount) { Console.WriteLine("Overdraft"); }
public void Transfer(BankCardBase card, double amount) { Console.WriteLine("Transfer"); }
public void Withdraw(double amount) { Console.WriteLine("Withdraw"); }
}
复制代码


建设银行龙卡信用卡实体类,继承自银行卡抽象基类并实现 ICreditCard 信用卡接口。


总结

经过以上的代码改造之后,银行卡基类 BankCardBase 只包含银行卡的基本功能,银行卡接口 ICard 只包含银行卡相关的业务操作,而信用卡接口 ICreditCard 则只包含与信用卡相关的业务操作,相互之间没有影响,修改普通银行卡或信用卡的功能,不会影响到其它类型的银行卡的业务逻辑,符合开闭原则。


转账方法使用基类参数 BankCardBase,运用在“运行时子类对象替代父类对象”的特点,使得代码的可扩展性极高,符合里氏替换原则。在这个示例中,我们优先使用各种类和接口的组合来代替之前的单一基类的继承关系来打造普通银行卡和信用卡的功能,使得整个设计更干净利落,可维护性高,并且这些类和接口还可以被无限合理的复用,这就是合成复用原则。

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2022.03.01 加入

该博客包括:.NET、Java、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、python、大数据等相关使用及进阶知识。查看博客过程中,如有任何问题,皆可随时沟通。

评论

发布
暂无评论
【愚公系列】2022年06月 面向对象设计原则(六)-合成复用原则_6月月更_愚公搬代码_InfoQ写作社区