跳转至

七大面向对象编程原则

单一职责(Single Responsiblity Principle)

  • 内容:一个类应该有且仅有一个引起它变化的原因
  • 带来的效果:
    • 高内聚:避免大而全,避免不相关功能的耦合
    • 低耦合:减少所需要依赖和被依赖的类
  • 作用:
    • 降低类的复杂性
    • 提高可读性和可维护性
    • 降低变更引起的风险
  • 如果一个类承担多个职责的缺点:
    • 一个职责的变化可能会削弱或抑制这个类实现其他职责的能力
    • 当客户端需要这个类实行某一个职责的时候,引入了不必要的职责,造成了冗余的代码
  • 代码:
    • 修改前
      interface Laptop{
          String cpu = "";
          int size = 0;
          void startUp();
          void shutDown();
      }
      
      class BasicLaptop implements Laptop{
          String cpu = "";
          int size = 0;
          void startUp();
          void shutDown();
      }
      
    • 使用了单一职责后:
      public class MacBookV1 {
      
          private MacLaptopProperty property;
          private MacLaptopFunction function;
      
          public static void main(String[] args) {
              System.out.println("Hello");   
          }
          public MacBookV1(MacLaptopProperty p, MacLaptopFunction f) {
              this.property = p;
              this.function = f;
          }
      }
      
      
      interface LaptopProperty {
          String cpu = "";
          int size = 0;
          String resolution = "";
      }
      interface LaptopFunction {
          void startUp();
          void shutDown();
      }
      
      class MacLaptopProperty implements LaptopProperty {
          String cpu = "";
          int size = 0;
          String resolution = "";
      }
      
      class MacLaptopFunction implements LaptopFunction {
          public void startUp() {};
          public void shutDown() {};
      }
      

开闭原则(Open-Closed Principle)

  • 开闭原则:应当对拓展开放,对修改关闭
    • 对拓展开放:通过拓展的方式应对需求的增加
    • 对修改的关闭:需求变化时,不修改原代码
  • 优点:
    • 测试简单
    • 可复用性变强
    • 稳定性变高
  • 实现的方式:接口和抽象类
  • 代码:
    • 修改前:
      public class NotificationService1 {
      
          public static void main(String[] args) {
              NotificationService1 ns = new NotificationService1();
              ns.sendNotification("email", "helo");
              ns.sendNotification("message", "hello");
          }
      
      //丑陋的if else
          public void sendNotification(String serviceType, String info) {
              if (serviceType == "email") {
                  sendEmail(info);
              } else if (serviceType == "message") {
                  sendMessage(info);
              }
          }
      
          public void sendEmail(String info) {
              EmailService es = new EmailService();
              es.sendNotification(info);
          }
      
          public void sendMessage(String info) {
              MessageService ms = new MessageService();
              ms.sendNotification(info);
          }
      }
      
      class EmailService {
          public void sendNotification(String info) {
              System.out.printf("Email: %s\n", info);
          }
      }
      
      class MessageService {
          public void sendNotification(String info) {
              System.out.printf("Message: %s\n", info);
          }
      }
      
    • 修改后:
      public class NotificationService2 {
      
          public static void main(String[] args) {
              NotificationService2 ns = new NotificationService2();
              ns.sendNotification(new EmailService(), "helo");
              ns.sendNotification(new MessageService(), "hello");
              ns.sendNotification(new WechatService(), "hello");
          }
      //直接使用接口的不同实现类的方法,避免了if else的出现,并且无须修改原码,就可以达到添加功能的目的
          public void sendNotification(NotificationService ns, String info) {
              ns.sendNotification(info);
          }
      }
      
      interface NotificationService {
          public void sendNotification(String info);
      }
      
      class EmailService implements NotificationService {
          public void sendNotification(String info) {
              System.out.printf("Email: %s\n", info);
          }
      }
      
      class MessageService implements NotificationService {
          public void sendNotification(String info) {
              System.out.printf("Message: %s\n", info);
          }
      }
      
      class WechatService implements NotificationService {
          public void sendNotification(String info) {
              System.out.printf("Wechat: %s\n", info);
          }
      }
      
  • 注意事项:
    • 用抽象构建框架,用实现拓展细节
    • 参数类型、引用对象尽量使用接口或者抽象类
    • 抽象层尽量保持稳定:接口和抽象类只负责定义方法,不负责具体的实现

迪米特法则(Law of Demeter)

  • 迪米特法则:一个类对于其他类知道的越少越好(只与直接的朋友通信)
    • 朋友:有耦合(依赖、关联、组合、聚合)关系的对象
    • 直接的朋友:成员变量、方法参数、方法返回值中的类
  • 好处:
    • 降低耦合度,提高模块功能的独立性
    • 实用,能够直接用于开发环境
  • 代码:
    • 修改前:
      public class SurfShop {
      
          public static void main(String[] args) {
              SurfShop surfShop = new SurfShop();
              Customer customer = new Customer();
              surfShop.chargeCustomer(customer, 10);
              System.out.println("Done");
          }
      //修改了参数c的成员card,并且不易懂
          public void chargeCustomer(Customer c, float fee) {
              c.getCard().balance -= fee;
          }
      }
      
      class Customer {
      
          private Card card = new Card();
      
          public Card getCard() {
              return this.card;
          }
      
      }
      
      class Card {
      
          public int balance = 10;
      
      }
      
    • 修改后:
      public class SurfShop2 {
      
          public static void main(String[] args) {
              SurfShop2 surfShop2 = new SurfShop2();
              Customer customer = new Customer();
              surfShop2.chargeCustomer(customer, 10);
              System.out.println("Done");
          }
      //通过中介c来实现
          public void chargeCustomer(Customer c, float fee) {
              c.pay(fee);
          }
      }
      
      class Customer {
      
          private Card card = new Card();
          //添加了一个支付方法
          public void pay(float fee) {
              this.card.deduct(fee);
          }
      
      }
      
      class Card {
      
          public int balance = 10;
      
          public void deduct(float fee) {
              this.balance -= fee;
          }
      
      }
      
  • 迪米特法则:
    • 不要:
      • objectA.getB().do()
      • objectA.getB().getC().do()
    • 门面模式
    • 中介模式
      • 可能存在大量的中介

依赖倒置原则(Dependency Inversion Principle)

  • 依赖倒置原则:
    • 高层不应该依赖底层,两者应该基于抽象
    • 抽象不应该依赖细节,细节依赖抽象(抽象比细节稳定)
  • 优点:
    • 架构更稳定
    • 更好的应对需求变化
  • 代码:
    • 修改前:
      public class CarRentalAgency {
      
          public static void main(String[] args) {
              CarRentalAgency agency =  new CarRentalAgency();
              agency.rentCar("BMW", "X5");
              agency.rentCar("Mercedes", "GLE");
          }
      
          public void rentCar(String brand, String model) {
              if (brand == "BMW") {
                  new BMW().rentBMW(model);
              } else if (brand == "Mercedes") {
                  new Mercedes().rentMercedes(model);
              }
          }
      }
      
      //用两个不同的类表示具有同样事物的属性,并且实现相同
      //若要添加新的汽车品牌,需要修改上面的ifelse块
      class BMW {
      
          public void rentBMW(String model) {
              System.out.println("BMW rented " + model);
          }
      }
      
      
      class Mercedes {
      
          public void rentMercedes(String model) {
              System.out.println("Mercedes rented " + model);
          }
      }
      
    • 修改后:
      public class CarRentalAgency2 {
      
          public static void main(String[] args) {
              CarRentalAgency2 agency = new CarRentalAgency2();
              agency.rentCar(new BMW(), "X5");
              agency.rentCar(new Mercedes(), "GLE");
              agency.rentCar(new Honda(), "Accord");
          }
      //用接口作为参数,调用方法,避免了ifelse
          public void rentCar(CarManufactory cm, String mode) {
              cm.rent(mode);
          }
      
      }
      //工厂模式,使用接口
      interface CarManufactory {
          public void rent(String model);
      }
      
      class BMW implements CarManufactory {
      
          public void rent(String model) {
              System.out.println("BMW rented " + model);
          }
      }
      
      class Mercedes implements CarManufactory{
      
          public void rent(String model) {
              System.out.println("Mercedes rented " + model);
          }
      }
      
      class Honda implements CarManufactory {
      
          public void rent(String model) {
              System.out.println("Honde rented " + model);
          }
      }
      

合成复用原则(Composite Reuse Principle)

  • 合成复用原则:尽量使用对象聚合/组合,而不是继承关系达到软件复用的目的

    [!聚合 组合 继承] - 聚合(aggregation):has-A - 可以独立于父组件存在 - 组合(composition):contains-A - 必要的组成部分 - 继承(ingeritance):is-A - 互相独立存在

\(\(聚合/组合>继承\)\) - 代码: - 修改前:

package v6_composite_reuse.java;

public class CarMaker {

    public static void main(String[] args) {
        RedGasolineCar c1 = new RedGasolineCar();
        c1.move();
    }

}

class GasolineCar {
    public void move() {
        System.out.println("gasoline move");

    };
}

class ElectricCar {
    public void move() {
        System.out.println("electric move");
    }
}

class RedGasolineCar extends GasolineCar {
    public void move() {
        System.out.println("red gasoline move");
    }
}

class BlueGasolineCar extends GasolineCar {
    public void move() {
        System.out.println("blue gasoline move");
    }
}
- 修改后:
package v6_composite_reuse.java;

public class CarMaker2 {

    public static void main(String[] args) {
        Car c = new Car(new Gasoline(), new Blue());
        c.move();
    }

}

class Car {
    private Energy energy;
    private Color color;
    public Car(Energy e, Color c) {
        this.energy = e;
        this.color = c;
    }
    public void move() {
        System.out.println(energy.toString() + color.toString() + "move");
    }
}

interface Energy {}

class Gasoline implements Energy { 
    public String toString() {return "gasoline";}
}

class Electric implements Energy { 
    public String toString() {return "electric";}
}

interface Color {}

class Red implements Color {
    public String toString() {return "red";}
}

class Blue implements Color {
    public String toString() {return "blue";}
}

接口隔离原则(Interface Segregation Principle)

  • 接口隔离原则:(\(多个专门的接口>单一的总接口\)\)
    • 客户端不应该依赖它不用的接口->低耦合
      • 如果客户依赖了不需要的接口,就要面临不需要接口变动带来的风险
      • 提倡客户不应被迫使用对其无用的方法或功能
      • 如果部分接口只被部分调用者使用,就需要把这部分的接口隔离出来
    • 类之间的依赖应该建立在最小的接口上面->高内聚
      • 把没有关系的接口合并在一起,会行程一个臃肿的大街口,这是对职责分配和接口的污染
      • 最小接口:满足项目需求的相似功能
      • 把臃的接口拆成更下和具体的接口
  • 代码:
    • 修改前:
      package v7_interface_segregation.java;
      
      public class Game1 {
      
        public static void main(String[] args) {
          Monster m = new Monster();
          m.basicAttack();
          MonsterBoss b = new MonsterBoss();
          b.magicAttack();
        }
      
      }
      
      interface BadCharacterSkill {
        public void basicAttack();
        public void magicAttack();
        public void recover();
      }
      //Monster被迫实现不需要的功能
      class Monster implements BadCharacterSkill {
        public void basicAttack() {
          System.out.println("Monster basic attack");
        }
        public void magicAttack() {};
        public void recover() {};
      }
      
      class MonsterBoss implements BadCharacterSkill {
        public void basicAttack() {
          System.out.println("Boss basic attack");
        }
        public void magicAttack() {
          System.out.println("Boss magic attack");
        }
        public void recover() {
          System.out.println("Boss recover");
        };
      }
      
    • 修改后:
      package v7_interface_segregation.java;
      
      public class Game2 {
      
          public static void main(String[] args) {
              Monster m = new Monster();
              m.basicAttack();
              MonsterBoss b = new MonsterBoss();
              b.magicAttack();
          }
      
      }
      //将基础与高级拆分开
      interface BasicBadCharacterSkill {
          public void basicAttack();
      }
      
      interface AdvancedBadCharacterSkill {
          public void magicAttack();
          public void recover();
      }
      
      class Monster implements BasicBadCharacterSkill {
          public void basicAttack() {
              System.out.println("Monster basic attack");
          }
      }
      
      class MonsterBoss implements BasicBadCharacterSkill, AdvancedBadCharacterSkill {
          public void basicAttack() {
              System.out.println("Boss basic attack");
          }
          public void magicAttack() {
              System.out.println("Boss magic attack");
          }
          public void recover() {
              System.out.println("Boss recover");
          };
      }
      

里式替换原则(Liskov Substitution Principle)

  • 里式替换原则:任何基类可以出现的地方,子类一定可以出现
  • 优点:
    • 在代码中引入继承关系会给程序带来侵入性
    • 保证程序升级后的兼容性
    • 避免程序出错
  • 原则:
    • 保证基类所拥有的性质在子类中依然成立
    • 子类拓展父类的功能,但是不能改变父类原有的功能
  • 如何规范的遵循里式替换原则:
    • 子类必须完全实现父类的抽象方法,但是不能覆盖父类的非抽象方法
      package v8_liskov_substitution_principle.java;
          public class Main1 {
              public static void main(String[] args) {
                  Calculator c1 = new Calculator();
                  System.out.println(c1.calculate(5, 10));
                  SuperCalculator c2 = new SuperCalculator();
                  System.out.println(c2.calculate(5, 10));
              }
          }
      
          class Calculator {
              public int calculate(int n1, int n2) {
                  return n1 + n2;
              }
          }
      
          class SuperCalculator {
              public int calculate(int n1, int n2) {
                  return n1 - n2;
              }
      }
      
    • 子类可以实现自己特有的方法
      package v8_liskov_substitution_principle.java;
      
      public class Main2 {
      
      }
      
      class Calculator {
          public int calculate(int n1, int n2) { return n1 + n2; }
      }
      
      class SuperCalculator extends Calculator {
          private int sum = 10;
          public int add(int n1, int n2) { return n1 + n2;}
          public int subtract(int n1, int n2) { return n1 - n2;}
      }
      
    • 当子类的方法实现父类的抽象方法时,方法的后置条件要比父类更严格
      • 不可以在子类中修改方法的返回类型
        package v8_liskov_substitution_principle.java;
        import java.util.ArrayList;
        import java.util.Collections;
        import java.util.List;
        
        public class Main3 {
            public static ArrayList<String> stringToList(Calculator c, String s) {
                return c.stringToList(s);
            }
        }
        
        abstract class Calculator {
            public abstract ArrayList<String> stringToList(String s);
        }
        
        class SuperCalculator extends Calculator {
            public List<String> stringToList(String s) {
                // do something
            }
        }
        
    • 子类的实例可以替代任何父类的实例,但反之不成立
      package v8_liskov_substitution_principle.java;
      
      public class Main4 {
      
          public static void main(String[] args) {
              Calculator c1 = new Calculator();
              int x = 1;
              int y = 2;
              int addResult = c1.calculate(x, y);
              System.out.println(addResult);
      
              SuperCalculator c2 = new SuperCalculator();
              int subtractResult = c2.subtract(x, y);
              System.out.println(subtractResult);
          }
      }
      
      class Calculator {
          public int calculate(int n1, int n2) { return n1 + n2; }
      }
      
      class SuperCalculator extends Calculator {
          public int subtract(int n1, int n2) { return n1 - n2;}
      }
      

      [!如何写出KISS(Keep It Simple asn Stupid)原则的代码] - 不要使用其他人可能不懂的技术来实现代码 - 不要重复造轮子,善于使用已有的工具类库 - 不要过度优化(如使用位运算代替算术运算)