6大设计原则(二)---里氏替换原则
里氏替换原则英文名称(Liskov Substitution Principle,LSP): 我的理解: 父类出现的地方也可以用子类来替换,而子类出现的地方父类不一定能替换。 里氏替换原则的为继承定义的规范,包括4层含义 1、子类必须完全实现父类的方法 2、子类可以有自己的个性 3、覆盖或实现父类的方法时输入参数可以被放大 4、覆写或实现父类的方法时输出结果可以被缩小 *模拟CS游戏
public class Test { public static void main(String[] args) { Soldier s = new Soldier(); s.setGun(new Handgun()); s.killEnemy(); } } abstract class AbstractGun{ public void shoot(){}; } /** * 战士类具有杀死敌人的方法,设置不同类别的枪的方法 * @author admin * */ class Soldier{ private AbstractGun gun; public void setGun(AbstractGun gun){ this.gun = gun; } public void killEnemy(){ gun.shoot(); System.out.println("正在射杀敌人..."); } } class MachineGun extends AbstractGun{ @Override public void shoot() { System.out.println("机关枪扫射..."); } } class Rifle extends AbstractGun{ @Override public void shoot() { System.out.println("步枪射击..."); } } class Handgun extends AbstractGun{ @Override public void shoot() { System.out.println("手枪射击..."); } } 子类必须完全实现父类的方法当如果又来了一个玩具枪时,我们一贯的思想是,将玩具枪继承AbstractGun类,实现shoot方法。
子类可以有自己的个性这句话很好理解,子类继承父类,不仅拥有父类的方法和属性,而且自己还可以用于其他的方法和属性 <span style="font-size:18px;">public class Test { public static void main(String[] args) { /* Soldier s = new Soldier(); s.setGun(new Handgun()); s.killEnemy(); System.out.println("----------------");*/ Snipper juji = new Snipper(); juji.setGun(new AUG()); juji.killEnemy(); } } abstract class AbstractGun{ public void shoot(){}; } class Snipper{ private AUG aug; public void setGun(AUG gun){ this.aug = gun; } public void killEnemy(){ aug.zoomOut(); aug.shoot(); System.out.println("狙击手正在射杀敌人..."); } } class AUG extends Rifle{ @Override public void shoot() { System.out.println("狙击枪射击..."); } public void zoomOut(){ System.out.println("狙击枪正在瞄准"); } }</span>覆盖或实现父类的方法时输入参数可以被放大 ---这句话类似于子类重载父类的方法时,保证子类的传入参数的范围大于父类的参数。 例如两个代码的比较:
package hpu.lzl.lsp; import java.util.Collection; import java.util.HashMap; import java.util.Map; public class Test02 { public static void main(String[] args) { Father f = new Father(); HashMap<String,String> hashMap = new HashMap<String,String>(); f.doSomething(hashMap); System.out.println("里氏替换----------------父类存在的地方子类应该也可以存在-----------"); Son s = new Son(); s.doSomething(hashMap); } } /** * 定义一个父类,实现将map集合转换成Collection集合 * @author admin * */ class Father{ public Collection doSomething(HashMap<String,String> map){ System.out.println("父类方法被执行..."); return map.values(); } } /** * 子类重载父类的方法 * @author admin * */ class Son extends Father{ /** * 注意此处是重载,返回值类型,方法名相同,传入参数不同。 * 保证传入的参数类型的范围大于父类。 */ public Collection doSomething(Map<String,String> map) { System.out.println("子类方法被执行..."); return map.values(); } }输出结果:
父类方法被执行... 里氏替换----------------父类存在的地方子类应该也可以存在----------- 父类方法被执行...方法二:
package hpu.lzl.lsp; import java.util.Collection; import java.util.HashMap; import java.util.Map; public class Test02 { public static void main(String[] args) { Father f = new Father(); HashMap<String,String>(); f.doSomething(hashMap); System.out.println("里氏替换----------------父类存在的地方子类应该也可以存在-----------"); Son s = new Son(); s.doSomething(hashMap); } } /** * 定义一个父类,实现将map集合转换成Collection集合 * @author admin * */ class Father{ public Collection doSomething(Map<String,String> map){ System.out.println(".......父类方法被执行..."); return map.values(); } } /** * 子类重载父类的方法 * @author admin * */ class Son extends Father{ /** * 注意此处是重载,返回值类型,方法名相同,传入参数不同。 */ public Collection doSomething(HashMap<String,String> map) { System.out.println("子类方法被执行..."); return map.values(); } }输出结果:
.......父类方法被执行... 里氏替换----------------父类存在的地方子类应该也可以存在----------- 子类方法被执行... 覆写或实现父类的方法时输出结果可以被缩小分两类解释:一,子类覆写父类的方法时,要求子类与父类的方法名相同,输入的参数相同,返回值值范围小于或等于父类的方法。二,子类重载父类的方法时,要求方法的输入参数类型或数量不同,在里氏替换原则要求下,子类的输入参数要大于或等于子类的输入参数。 我的理解:里氏替换原则,就是在继承的概念上有定义了一些开发时需要的规范。例如子类继承父类时,需要拥有父类的方法(这个意思是,在给对象分类时,一定要抽取相同的属性的方法。例如玩具枪虽然属性与其他枪相同,但是不能杀死敌人,因而不能继承AbstractGun类。)。严格按照这个规范来进行开发,为后期版本升级,增添子类时都可以很好的维护。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |