设计模式6大原则之里氏替换原则
里氏替换原则
定义: 所有引用基类的地方必须能透明地使用其子类的对象。 通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。 但是这里我们需要注意的是:有子类出现的地方,父类未必就能适应。 优点: 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性; 提高代码的重用性; 子类可以形似父类,但又异于父类; 提高代码的可扩展性; 提高产品或项目的开放性。 缺点:继承是侵入性的,只要继承就必须拥有父类的所有属性和方法; 降低代码的灵活性,子类必须拥有父类的属性和方法,让子类增加了约束; 增强了耦合性,当父类的常量、变量和方法被修改时,必须考虑子类的修改。 含义: 1、子类必须完全实现父类的方法 例:
如上图,我们知道枪有很多种,而且有一个共同的功能就是射击。Soldier中我们添加了一个KillEnemy()方法,这个方法使用枪来杀敌人,具体使用哪个枪取决于选用哪个 枪。 代码:
public abstract class AbstractGun { public abstract void Shoot(); } class Handgun : AbstractGun { public override void Shoot() { Console.WriteLine("手枪射击"); } } class Rifle:AbstractGun { public override void Shoot() { Console.WriteLine("步枪射击"); } } class MachineGun : AbstractGun { public override void Shoot() { Console.WriteLine("机枪射击"); } } public class Soldier { private AbstractGun gun; public AbstractGun Gun { set { this.gun = value; } get { return gun; } } public void KillEnemy() { Console.WriteLine("士兵开始射击"); gun.Shoot(); } }客户端:
static void Main(string[] args) { Soldier soldier = new Soldier(); soldier.Gun = new Handgun(); soldier.KillEnemy(); } 注意:在类中调用其他类时务必要用父类或接口,若不能使用,则说明类的设计已经违背LSP原则。 但是现在有一把玩具枪,但是我们知道玩具枪是不能像其它的枪一样射击并击杀敌人的,所以玩具枪不能真正实现Shoot方法。
class ToyGun : AbstractGun { public override void Shoot() { //玩具枪不能射击杀人,所以不能真正实现此方法 } }
在这种情况下,我们发现业务调用类已经出现问题了,正常的业务逻辑不能运行。这里有两种处理方法: (1)在Soldier类中增加判断,如果是仿真枪,就不用来杀人。这个方法可以解决问题,但是在程序中,我们每增加一个类,所有与这个父类有关系的类都必须修改,这样就不可行了。所以这个方案被否定了。 (2)ToyGun脱离继承,建立一个独立的父类,可以与AbstractGun建立关联委托关系。类图如下: 2、子类可以有自己的个性 因为有这个规则,里氏替换原则不能反过来使用,在子类出现的地方,父类不一定可以胜任。 3、覆盖或者实现父类的方法的时候输入的参数可以被放大
|