设计模式学习笔记之里式替换原则

在面向对象的语言中,继承是必不可少的、非常优秀的语言机制,它的优缺点如下:
优点:
(1)代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性。
(2)提高代码的重用性
(3)提高代码的可扩展性,实现父类的方法就可以“为所欲为”了。君不见很多开源框架的扩展接口都是通过继承父类来完成的。
(4)提高产品或项目的开放性。
缺点:
(1)继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。
(2)降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束。
(3)增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改。,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——一大片的代码需要重构。 
里式替换原则有两种定义:
(1)如果对每一个类型为S的对象O1,都有类型为T的对象O2,使得以T定义的所有程序P在所有的对象O1都代换成O2时,程序P的行为没有发生变化,那么类型T是类型S的子类型。
(2)所有引用基类的地方必须能透明地使用其子类的对象。
第二个定义是最清晰明确的,通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
里式替换原则为良好的继承定义了一个规范,一句简单的定义包含了4层含义:(1)子类必须完全实现父类的方法(2)子类可以有自己的个性(3)覆盖或实现父类的方法时输入参数可以被放大(4)覆写或实现父类的方法时输出结果可以被缩小。
覆盖或实现父类的方法时输入参数可以被放大:方法中的输入参数称为前置条件。里式替换原则也要求制定一个契约,就是父类或接口,这种设计方法也叫做Design by Contract(契约设计),与里式替换原则有着异曲同工之妙。契约制定了,也就同时制定了前置条件和后置条件,前置条件就是你要让我执行,就必须满足我的条件;后置条件就是我执行完了需要反馈,标准是什么。
覆写或实现父类的方法时输出结果可以被缩小:这是什么意思呢,父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里式替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型,要么S是T的子类,为什么呢?分两种情况,如果是覆写,父类和子类的同名方法的输入参数是相同,两个方法的范围值S小于等于T,这是覆写的要求,这才是重中之重,子类覆写父类的方法,天经地义。如果是重载,则要求方法的输入参数类型或数量不相同,在里式替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数,也就是说你写的这个方法时不会被调用的。
练习代码:
package com.gc.designmodel;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/***
 *
    * @说明 学习设计模式之里式替换原则,LSP(Liskov Substitution Principle,LSP)
    * @作者 GeneralAndroid
    * @创建日期 2015-8-31 下午1:37:40
    * @版本号 1.0
    * @since 1.0
    * 采用里式替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。
    * 即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,
    * 使用父类作为参数,传递不同的子类完成不同的业务逻辑,非常完美!
 */
public class TestLSPPrinciple {

 public static void main(String[] args) {
  //产生三毛这个士兵
//	 Soldier sanMao=new Soldier();
//	 //给三毛一支枪
//	 sanMao.setGun(new Rifle());
//	 sanMao.killEnemy();
//	 Snipper sanMao=new Snipper();
//	 sanMao.setRifle(new AUG());
//	 sanMao.killEnemy(new AUG());
  invoker1();
 }
 public static void invoker1()
 {
//	 Father1 f=new Father1();
  Son1 f=new Son1();
  HashMap map=new HashMap();
  f.doSomething(map);
 }
 public static void invoker()
 {
  //有父类的地方就有子类
//	 Father f=new Father();
  Son f=new Son();
  HashMap map=new HashMap();
  f.doSomething(map);
 }
 
 
}
/**枪支抽象类**/
abstract class AbstractGun{
 /**枪用来干什么的?杀敌!**/
 public abstract void shoot();
}
/**手枪**/
class Handgun extends AbstractGun
{

 /**手枪的特点是携带方便、射程远**/
 public void shoot() {
  // TODO Auto-generated method stub
  System.out.println("手枪射击...");
 }
 
}
/**步枪**/
class Rifle extends AbstractGun
{

 /**步枪的特点是射程远,威力大**/
 public void shoot() {
  // TODO Auto-generated method stub
  System.out.println("步枪射击...");
 }
 
}
/**机枪**/
class MachineGun extends AbstractGun
{

 /**机枪扫射**/
 public void shoot() {
  // TODO Auto-generated method stub
  System.out.println("机枪扫射...");
 }
 
}
/**
 * 士兵类
 * 注意:(1)在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的
 * 设计已经违背了LSP原则。(2)如果子类不能完整地实现父类的方法,或者父类的某些方法在子类
 * 中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
 * **/
class Soldier
{
 /**定义士兵的枪支**/
 private AbstractGun gun;
 /**给士兵一支枪**/
 public void setGun(AbstractGun _gun)
 {
  this.gun=_gun;
 }
 public void killEnemy()
 {
  System.out.println("士兵开始杀敌人....");
  gun.shoot();
 }
}
class AUG extends Rifle
{
 /**狙击枪都携带一个精准的望远镜**/
 public void zoomOut()
 {
  System.out.println("通过望远镜察看敌人...");
 }
 public void shoot()
 {
  System.out.println("AUG射击...");
 }
}
class Snipper
{
 private Rifle rifle;
 public void setRifle(Rifle rifle)
 {
  this.rifle=rifle;
 }
 public void killEnemy(AUG aug)
 {
  /**首先看看敌人的情况,别杀死敌人,自己也被干掉**/
  aug.zoomOut();
  /**开始射击**/
  aug.shoot();
 
 }
}
/***覆盖或实现父类的方法时输入参数可以被放大
 * 里式替换原则也要求制定一个契约,就是父类或接口,这种设计方法也叫做Design by Contract(契约设计)
 * 与里式替换原则有着异曲同工之妙。契约制定了,也就同时制定了前置条件和后置条件,前置条件就是你要让我执行,
 * 就必须满足我的条件,后置条件就是我执行完了需要反馈。
 * 注意:子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松。
 * ***/
class Father
{
 public Collection doSomething(HashMap map)
 {
  System.out.println("父类被执行...");
  return map.values();
 }
}
class Son extends Father
{
 /**放大输入参数类型**/
 public Collection doSomething(Map map)
 {
  System.out.println("子类被执行...");
  return map.values();
 }
}
class Father1
{
 public Collection doSomething(Map map)
 {
  System.out.println("父类1被执行...");
  return map.values();
 }
}
class Son1 extends Father1
{
 public Collection doSomething(HashMap map)
 {
  System.out.println("子类1被执行...");
  return map.values();
 }
}
转载注明出处: http://blog.csdn.net/gc_gongchao/article/details/48164463

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值