面向对象七大设计原则,看了必会(代码详细版)(中)

简介: 面向对象七大设计原则,看了必会(代码详细版)(中)

正确示范

public class OpenClosed2 {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());//+新增绘制三角形
    }
}
//这是一个用于绘图的类
class GraphicEditor {
    //接收 Shape 对象,调用 draw 方法
    public void drawShape(Shape s) {
        s.draw();
    }
}
abstract class Shape {
    int m_type;
    public abstract void draw();
}
//以前就写好的
class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}
//以前就写好的
class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}
//+新增绘制三角形
class Triangle extends Shape {
    Triangle() {
        super.m_type = 3;
    }
    @Override
    public void draw() {
        System.out.println("绘制三角形");
    }
}
绘制矩形
绘制圆形
绘制三角形

3、里氏替换原则

定义

子类可以扩展父类的功能,但不能改变原有父类的功能。

详细描述

里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

根据上述理解,对里氏替换原则的定义可以总结如下:

● 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

● 子类中可以增加自己特有的方法

● 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松

● 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

枪支抽象类:
public abstract class AbstractGun {
    public abstract  void shoot();
}
手枪实现类:
public class HandGun extends AbstractGun {
    public void shoot() {
       System.out.println("手机射击");     
   }
}
public class Rifle extends AbstractGun {
    public void shoot() {
       System.out.println("步枪射击");     
   }
}
士兵实现类:
public class Soldier {
  private AbstractGun gun;
  public void setGun(AbstractGun gun) {
    this.gun = gun;
  }
  public void killEnemy() {
    System.out.println("士兵杀敌人");
    gun.shoot();
  }
}
场景类:
  public class Client {
    public static void main(String[] args) {
      Soldier sanMao = new Soldier();
      sanMao.setGun(new Rifle());
      sanMao.killEnemy();
  }
}

注意

在类中调用其他类时务必要使用父类或者接口(例如Solider类的setGun(AbstractGun gun)方法),否则说明类的设计已经违背了LSP原则。

现在有个玩具枪该怎么定义?直接继承AbstractGun类吗?如下:

public class ToyGun extends AbstractGun {
  @Override
  public void shoot() {
    //玩具枪不能像真枪杀敌,不实现
  }
}
场景类:
  public class Client {
    public static void main(String[] args) {
      Soldier sanMao = new Soldier();
      sanMao.setGun(new ToyGun());
      sanMao.killEnemy();
  }
}

在这种情况下,士兵拿着玩具枪杀敌,发现业务调用类已经出现了问题,正常的业务逻辑运行结果是不正确的。(因为玩具枪并不能杀敌)ToyGun应该脱离继承,建立一个独立的类,可以与AbstractGun建立关联委托关系。类图如下:

注意

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中发生重写或者重载,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。

2、子类中可以增加自己特有的方法

说下面两层含义之前先要明白 重载 重写(覆盖) 的区别:

重写(覆盖)的规则:

1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载.

2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。

3、重写的方法的返回值必须和被重写的方法的返回一致;

4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;

5、被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写。

6、静态方法不能被重写为非静态的方法(会编译出错)。

重载的规则:

1、在使用重载时只能通过相同的方法名、不同的参数形式实现。不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样);

2、不能通过访问权限、返回类型、抛出的异常进行重载;

3、方法的异常类型和数目不会对重载造成影响;

3、当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松

public class Father {
    public Collection doSomething(HashMap  map){
        System.out.println("父类被执行了");
        return map.values();
    }
}
public class Son  extends Father{
    public Collection doSomething(Map  map){
        System.out.println("子类被执行了");
        return map.values();
    }
}
public class Client{
    public static void main(String[] args) {
        invoker();
    }
    public  static void invoker(){
           Son son = new  Son();//子类对象
           HashMap  map=new HashMap<>();
           son.doSomething(map);
    }
}

运行是”父类被执行了”,这是正确的,父类方法的参数是HashMap类型,而子类的方法参数是Map类型,子类的参数类型范围比父类大,那么子类的方法永远也不会执行。

如果我们反过来让父类的参数类型范围大于子类,并在调用时用子类去调用,我们会发现打印时的结果是”子类被执行了”,这就违反了里氏替换原则,在开发中很容易引起业务逻辑的混乱,所以类的方法重载父类的方法时,方法的前置条件(形参)要比父类方法的输入参数更宽松(相同也可以)。

4、当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

父类的一个方法的返回值是一个类型T,子类的相同方法(重载或者重写)的返回值为S,那么里氏替换原则就要求S必须小于等于T。

4、接口隔离

原则介绍

客户端不应该依赖它不需要的接口, 即一个类对另一个类的依赖应该建立在最小的接口上。

示范

//A、B总的接口
interface InterfaceAll {
    void operation1();
    void operation2();
    void operation3();
}
//实现类A只用InterfaceAll中的operation1、operation2方法,所以实现两个方法
class A implements InterfaceAll {
    @Override
    public void operation1() {
        System.out.println("A 实现了 operation1...");
    }
    @Override
    public void operation2() {
        System.out.println("A 实现了 operation2...");
    }
    @Override
    public void operation3() {
        //A用不到,但是还需要空实现
    }
}
//实现类B只用InterfaceAll中的operation1、operation3方法,所以实现两个方法
class B implements InterfaceAll {
    @Override
    public void operation1() {
        System.out.println("B 实现了 operation1...");
    }
@Override
    public void operation2() {
        //B用不到,但是还需要空实现
    }
    @Override
    public void operation3() {
        System.out.println("B 实现了 operation3...");
    }
}
正确示范
//接口Interface1
interface Interface1 {
    void operation1();
}
//接口Interface2
interface Interface2 {
    void operation2();
}
//接口Interface3
interface Interface3 {
    void operation3();
}
//实现类A用Interface1中的operation1和Interface2中的operation2
class A implements Interface1, Interface2 {
    @Override
    public void operation1() {
        System.out.println("A 实现了 operation1...");
    }
    @Override
    public void operation2() {
        System.out.println("A 实现了 operation2...");
    }
}
//实现类B用Interface1中的operation1和Interface3中的operation3
class B implements Interface1, Interface3 {
    @Override
    public void operation1() {
        System.out.println("B 实现了 operation1...");
    }
    @Override
    public void operation3() {
        System.out.println("B 实现了 operation3...");
    }
}


相关文章
|
4月前
|
设计模式 存储 自然语言处理
Java面向对象设计七大原则
Java面向对象设计七大原则
70 0
|
9月前
|
关系型数据库
面向对象七大设计原则,看了必会(代码详细版)(上)
面向对象七大设计原则,看了必会(代码详细版)(上)
面向对象七大设计原则,看了必会(代码详细版)(下)
面向对象七大设计原则,看了必会(代码详细版)(下)
|
10月前
|
设计模式 数据库
面向对象的七大设计原则
面向对象的七大设计原则
264 0
|
设计模式 Java 关系型数据库
面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
|
设计模式 算法 Java
六大原则之外的设计原则|设计原则
在前面的几篇设计原则文章中,我们分别讲述了经典的六大设计原则。但是事实上,我们在开发中还有几个重要的设计原则,在这篇文章中,一并给大家讲述。
|
设计模式 前端开发 Java
面向对象设计的六大设计原则 (通俗易懂的版本)
六大原则虽说是原则,但它们并不是强制性的,更多的是建议。
面向对象的设计的7大原则
面向对象的设计的7大原则
191 0
|
设计模式 消息中间件 IDE
设计模式之美(一)——设计原则、规范与重构
  《设计模式之美》是极客时间上的一个代码学习系列,在学习之后特在此做记录和总结。
设计模式之美(一)——设计原则、规范与重构
|
设计模式
面向对象的设计原则最终篇(二)
关于面向对象的设计原则我之前已经解释过四种了,分别是单一职责原则,开放关闭原则,里式替换原则,依赖倒置原则而接下来我们要解释的就是最后的三种原则了,分别是接口隔离原则, 迪米特法则, 组合复用原则。
面向对象的设计原则最终篇(二)