6大设计原则(二)---里氏替换原则

前端之家收集整理的这篇文章主要介绍了6大设计原则(二)---里氏替换原则前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

里氏替换原则

英文名称(Liskov Substitution Principle,LSP):

定义:所有引用基类的地方必须能够透明地使用其子类的对象
我的理解: 父类出现的地方也可以用子类来替换,而子类出现的地方父类不一定能替换。

里氏替换原则的为继承定义的规范,包括4层含义
1、子类必须完全实现父类方法
2、子类可以有自己的个性
3、覆盖或实现父类方法时输入参数可以被放大

4、覆写或实现父类方法输出结果可以被缩小

*模拟CS游戏
*战士可以拿枪去杀敌人,setGun()给战士设置不同的枪。killEnemy()射杀敌人。
*抽象类,AbstractGun,void shoot()方法
*枪类MachineGun(机枪),Rifle(步枪),Handgun(手枪)实现抽象类方法
*-----这个类的设计原理是,每种枪都有一个“射击”的方法,因此抽象出来使用。


  1. public class Test {
  2. public static void main(String[] args) {
  3. Soldier s = new Soldier();
  4. s.setGun(new Handgun());
  5. s.killEnemy();
  6. }
  7. }
  8. abstract class AbstractGun{
  9. public void shoot(){};
  10. }
  11. /**
  12. * 战士类具有杀死敌人的方法,设置不同类别的枪的方法
  13. * @author admin
  14. *
  15. */
  16. class Soldier{
  17. private AbstractGun gun;
  18. public void setGun(AbstractGun gun){
  19. this.gun = gun;
  20. }
  21. public void killEnemy(){
  22. gun.shoot();
  23. System.out.println("正在射杀敌人...");
  24. }
  25. }
  26. class MachineGun extends AbstractGun{
  27. @Override
  28. public void shoot() {
  29. System.out.println("机关枪扫射...");
  30. }
  31. }
  32. class Rifle extends AbstractGun{
  33. @Override
  34. public void shoot() {
  35. System.out.println("步枪射击...");
  36. }
  37. }
  38. class Handgun extends AbstractGun{
  39. @Override
  40. public void shoot() {
  41. System.out.println("手枪射击...");
  42. }
  43. }

子类必须完全实现父类方法

当如果又来了一个玩具枪时,我们一贯的思想是,将玩具枪继承AbstractGun类,实现shoot方法
*但是依照情景玩具枪不具备射击的能力。因此玩具枪不能直接继承AbstractGun类。因为它不能完整的实现父类方法


子类可以有自己的个性

这句话很好理解,子类继承父类,不仅拥有父类方法属性,而且自己还可以用于其他的方法属性
* 并且可以覆写父类方法,重载父类方法等。

  1. <span style="font-size:18px;">public class Test {
  2. public static void main(String[] args) {
  3. /* Soldier s = new Soldier();
  4. s.setGun(new Handgun());
  5. s.killEnemy();
  6. System.out.println("----------------");*/
  7. Snipper juji = new Snipper();
  8. juji.setGun(new AUG());
  9. juji.killEnemy();
  10. }
  11. }
  12. abstract class AbstractGun{
  13. public void shoot(){};
  14. }
  15. class Snipper{
  16. private AUG aug;
  17. public void setGun(AUG gun){
  18. this.aug = gun;
  19. }
  20. public void killEnemy(){
  21. aug.zoomOut();
  22. aug.shoot();
  23. System.out.println("狙击手正在射杀敌人...");
  24. }
  25. }
  26. class AUG extends Rifle{
  27. @Override
  28. public void shoot() {
  29. System.out.println("狙击枪射击...");
  30. }
  31. public void zoomOut(){
  32. System.out.println("狙击枪正在瞄准");
  33. }
  34. }</span>
覆盖或实现父类方法时输入参数可以被放大

---这句话类似于子类重载父类方法时,保证子类的传入参数的范围大于父类的参数。
* 如果没有满足以上条件的话,就不满足父类存在的地方子类也存在的条件,因而违背了里氏替换原则。

例如两个代码的比较:

  1. package hpu.lzl.lsp;
  2.  
  3. import java.util.Collection;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6.  
  7. public class Test02 {
  8. public static void main(String[] args) {
  9. Father f = new Father();
  10. HashMap<String,String> hashMap = new HashMap<String,String>();
  11. f.doSomething(hashMap);
  12. System.out.println("里氏替换----------------父类存在的地方子类应该也可以存在-----------");
  13. Son s = new Son();
  14. s.doSomething(hashMap);
  15. }
  16. }
  17. /**
  18. * 定义一个父类,实现将map集合转换成Collection集合
  19. * @author admin
  20. *
  21. */
  22. class Father{
  23. public Collection doSomething(HashMap<String,String> map){
  24. System.out.println("父类方法被执行...");
  25. return map.values();
  26. }
  27. }
  28. /**
  29. * 子类重载父类方法
  30. * @author admin
  31. *
  32. */
  33. class Son extends Father{
  34. /**
  35. * 注意此处是重载,返回值类型,方法名相同,传入参数不同。
  36. * 保证传入的参数类型的范围大于父类
  37. */
  38. public Collection doSomething(Map<String,String> map) {
  39. System.out.println("子类方法被执行...");
  40. return map.values();
  41. }
  42. }
输出结果:

  1. 父类方法被执行...
  2. 里氏替换----------------父类存在的地方子类应该也可以存在-----------
  3. 父类方法被执行...
方法二:

  1. package hpu.lzl.lsp;
  2.  
  3. import java.util.Collection;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6.  
  7. public class Test02 {
  8. public static void main(String[] args) {
  9. Father f = new Father();
  10. HashMap<String,String>();
  11. f.doSomething(hashMap);
  12. System.out.println("里氏替换----------------父类存在的地方子类应该也可以存在-----------");
  13. Son s = new Son();
  14. s.doSomething(hashMap);
  15. }
  16. }
  17. /**
  18. * 定义一个父类,实现将map集合转换成Collection集合
  19. * @author admin
  20. *
  21. */
  22. class Father{
  23. public Collection doSomething(Map<String,String> map){
  24. System.out.println(".......父类方法被执行...");
  25. return map.values();
  26. }
  27. }
  28. /**
  29. * 子类重载父类方法
  30. * @author admin
  31. *
  32. */
  33. class Son extends Father{
  34. /**
  35. * 注意此处是重载,返回值类型,方法名相同,传入参数不同。
  36. */
  37. public Collection doSomething(HashMap<String,String> map) {
  38. System.out.println("子类方法被执行...");
  39. return map.values();
  40. }
  41. }
输出结果:

  1. .......父类方法被执行...
  2. 里氏替换----------------父类存在的地方子类应该也可以存在-----------
  3. 子类方法被执行...

覆写或实现父类方法输出结果可以被缩小

分两类解释:一,子类覆写父类方法时,要求子类与父类方法名相同,输入的参数相同,返回值值范围小于或等于父类方法。二,子类重载父类方法时,要求方法的输入参数类型或数量不同,在里氏替换原则要求下,子类的输入参数要大于或等于子类的输入参数。

我的理解:里氏替换原则,就是在继承的概念上有定义了一些开发时需要的规范。例如子类继承父类时,需要拥有父类方法(这个意思是,在给对象分类时,一定要抽取相同的属性方法。例如玩具枪虽然属性与其他枪相同,但是不能杀死敌人,因而不能继承AbstractGun类。)。严格按照这个规范来进行开发,为后期版本升级,增添子类时都可以很好的维护。

猜你在找的设计模式相关文章