七大面向对象编程原则
单一职责(Single Responsiblity Principle)
- 内容:一个类应该有且仅有一个引起它变化的原因
- 带来的效果:
- 高内聚:避免大而全,避免不相关功能的耦合
- 低耦合:减少所需要依赖和被依赖的类
- 作用:
- 降低类的复杂性
- 提高可读性和可维护性
- 降低变更引起的风险
- 如果一个类承担多个职责的缺点:
- 一个职责的变化可能会削弱或抑制这个类实现其他职责的能力
- 当客户端需要这个类实行某一个职责的时候,引入了不必要的职责,造成了冗余的代码
- 代码:
- 修改前
- 使用了单一职责后:
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)原则的代码] - 不要使用其他人可能不懂的技术来实现代码 - 不要重复造轮子,善于使用已有的工具类库 - 不要过度优化(如使用位运算代替算术运算)
- 子类必须完全实现父类的抽象方法,但是不能覆盖父类的非抽象方法