文章目录
-
-
- 前言
- [1. 开闭原则(Open/Closed Principle)](#1. 开闭原则(Open/Closed Principle))
- [2. 里氏代换原则(Liskov Substitution Principle)](#2. 里氏代换原则(Liskov Substitution Principle))
- [3. 依赖倒转原则(Dependency Inversion Principle)](#3. 依赖倒转原则(Dependency Inversion Principle))
- [4. 接口隔离原则(Interface Segregation Principle)](#4. 接口隔离原则(Interface Segregation Principle))
- [5. 迪米特法则(Law of Demeter)](#5. 迪米特法则(Law of Demeter))
- [6. 合成复用原则(Composition Over Inheritance)](#6. 合成复用原则(Composition Over Inheritance))
-
前言
软件设计原则有助于编写灵活、可扩展且易于维护的代码。以下是每个设计原则的定义以及未遵循和遵循原则的 Java 示例。
1. 开闭原则(Open/Closed Principle)
定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。意味着我们应通过添加新代码,而不是修改现有代码来实现功能的扩展。
违反开闭原则的示例
java
class Rectangle {
public double width;
public double height;
}
class Circle {
public double radius;
}
class AreaCalculator {
public double calculateArea(Object shape) {
if (shape instanceof Rectangle) {
Rectangle rectangle = (Rectangle) shape;
return rectangle.width * rectangle.height;
} else if (shape instanceof Circle) {
Circle circle = (Circle) shape;
return Math.PI * circle.radius * circle.radius;
}
return 0;
}
}
问题 :如果要添加新形状(例如三角形),必须修改 calculateArea
方法,违反了开闭原则。
遵循开闭原则的示例
java
interface Shape {
double calculateArea();
}
class Rectangle implements Shape {
public double width;
public double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
}
class Circle implements Shape {
public double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
class AreaCalculator {
public double calculateArea(Shape shape) {
return shape.calculateArea();
}
}
优势 :如果要添加新形状,只需创建一个新的实现类,而无需修改 AreaCalculator
类。
2. 里氏代换原则(Liskov Substitution Principle)
定义:子类必须能够替换其基类,而不会破坏应用程序的正确性。也就是说,程序中的对象应能够使用其子类实例替换而不影响功能。
违反里氏代换原则的示例
java
class Bird {
public void fly() {
System.out.println("Bird is flying");
}
}
class Ostrich extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Ostriches cannot fly");
}
}
问题 :Ostrich
是 Bird
的子类,但它无法飞行,导致在使用 Bird
类型时可能产生异常。
遵循里氏代换原则的示例
java
class Bird {
public void move() {
System.out.println("Bird is moving");
}
}
class FlyingBird extends Bird {
public void fly() {
System.out.println("Flying bird is flying");
}
}
class Ostrich extends Bird {
@Override
public void move() {
System.out.println("Ostrich is running");
}
}
class Sparrow extends FlyingBird {
@Override
public void fly() {
System.out.println("Sparrow is flying");
}
}
优势 :通过将 Bird
和 FlyingBird
分开处理,Ostrich
仍然遵循里氏代换原则,并且代码更具扩展性。
3. 依赖倒转原则(Dependency Inversion Principle)
定义:高层模块不应该依赖于低层模块。二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
违反依赖倒转原则的示例
java
class LightBulb {
public void turnOn() {
System.out.println("LightBulb is on");
}
public void turnOff() {
System.out.println("LightBulb is off");
}
}
class Switch {
private LightBulb bulb;
public Switch(LightBulb bulb) {
this.bulb = bulb;
}
public void press() {
bulb.turnOn();
}
}
问题 :Switch
类直接依赖于具体的 LightBulb
实现,违反了依赖倒转原则。
遵循依赖倒转原则的示例
java
interface Switchable {
void turnOn();
void turnOff();
}
class LightBulb implements Switchable {
@Override
public void turnOn() {
System.out.println("LightBulb is on");
}
@Override
public void turnOff() {
System.out.println("LightBulb is off");
}
}
class Fan implements Switchable {
@Override
public void turnOn() {
System.out.println("Fan is on");
}
@Override
public void turnOff() {
System.out.println("Fan is off");
}
}
class Switch {
private Switchable device;
public Switch(Switchable device) {
this.device = device;
}
public void press() {
device.turnOn();
}
}
优势 :Switch
类依赖于 Switchable
接口,而不是具体的实现。现在我们可以轻松替换为任何其他实现。
4. 接口隔离原则(Interface Segregation Principle)
定义:不应该强迫客户端依赖于他们不使用的接口。应将庞大的接口拆分成更小、更具体的接口,使得客户端只需知道它们真正需要的方法。
违反接口隔离原则的示例
java
interface Worker {
void work();
void eat();
}
class HumanWorker implements Worker {
@Override
public void work() {
System.out.println("Human is working");
}
@Override
public void eat() {
System.out.println("Human is eating");
}
}
class RobotWorker implements Worker {
@Override
public void work() {
System.out.println("Robot is working");
}
@Override
public void eat() {
// Robot does not eat, but must implement this method
throw new UnsupportedOperationException("Robot does not eat");
}
}
问题 :RobotWorker
实现了 eat()
方法,但它实际上并不需要这个功能。
遵循接口隔离原则的示例
java
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class HumanWorker implements Workable, Eatable {
@Override
public void work() {
System.out.println("Human is working");
}
@Override
public void eat() {
System.out.println("Human is eating");
}
}
class RobotWorker implements Workable {
@Override
public void work() {
System.out.println("Robot is working");
}
}
优势 :RobotWorker
只实现了它需要的 Workable
接口,不再需要实现不必要的 eat()
方法。
5. 迪米特法则(Law of Demeter)
定义:迪米特法则也称为"最少知识原则",它规定一个对象应该对其他对象有尽可能少的了解。具体而言,一个对象不应直接调用其他对象的内部方法或与其不直接相关的对象交互。
违反迪米特法则的示例:
java
class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public Engine getEngine() {
return engine;
}
}
class Driver {
private Car car;
public Driver(Car car) {
this.car = car;
}
public void startCar() {
car.getEngine().start(); // 直接调用 Engine 的方法
}
}
问题 :Driver
直接调用了 Engine
的 start()
方法,违反了迪米特法则。Driver
类不应该知道 Car
的内部实现细节(即 Engine
的存在)。
遵循迪米特法则的示例:
java
class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start(); // Car 自己管理 Engine 的启动
}
}
class Driver {
private Car car;
public Driver(Car car) {
this.car = car;
}
public void startCar() {
car.start(); // 只与 Car 对象直接交互
}
}
优势 :Driver
只与 Car
交互,而不是直接操控 Car
的内部组件 Engine
,从而遵循了迪米特法则。这样减少了对象之间的耦合度,提高了代码的可维护性。
6. 合成复用原则(Composition Over Inheritance)
定义:合成复用原则建议优先使用对象的组合而不是继承来实现代码复用。继承容易导致子类和父类之间的强耦合,而组合更灵活且降低了耦合性。
违反合成复用原则的示例
java
class Animal {
public void makeSound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark");
}
}
class RobotDog extends Dog {
// 机器人狗不是真正的狗,但却继承了 Dog
public void chargeBattery() {
System.out.println("RobotDog charging battery");
}
}
问题 :RobotDog
继承了 Dog
,但它并不需要 Dog
的所有行为,例如真实狗的叫声,这使得继承不适用。
遵循合成复用原则的示例
java
class AnimalSound {
public void makeSound() {
System.out.println("Animal sound");
}
}
class Dog {
private AnimalSound sound;
public Dog(AnimalSound sound) {
this.sound = sound;
}
public void makeSound() {
sound.makeSound();
}
}
class RobotDog {
public void chargeBattery() {
System.out.println("RobotDog charging battery");
}
}
优势 :RobotDog
和 Dog
不再有强制的继承关系。Dog
使用组合来实现 makeSound()
方法,这使得它与其他对象(如 RobotDog
)更加独立,减少了类之间的耦合。