软件设计原则(Java实现/给出正例反例)

文章目录

前言

软件设计原则有助于编写灵活、可扩展且易于维护的代码。以下是每个设计原则的定义以及未遵循和遵循原则的 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");
    }
}

问题OstrichBird 的子类,但它无法飞行,导致在使用 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");
    }
}

优势 :通过将 BirdFlyingBird 分开处理,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 直接调用了 Enginestart() 方法,违反了迪米特法则。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");
    }
}

优势RobotDogDog 不再有强制的继承关系。Dog 使用组合来实现 makeSound() 方法,这使得它与其他对象(如 RobotDog)更加独立,减少了类之间的耦合。

相关推荐
nvd113 分钟前
Java ETL - Apache Beam 简介
java·apache·etl
晴子呀19 分钟前
Spring底层原理大致脉络
java·后端·spring
只吹45°风26 分钟前
Java-ArrayList和LinkedList区别
java·arraylist·linkedlist·区别
阿华的代码王国33 分钟前
【JavaEE】多线程编程引入——认识Thread类
java·开发语言·数据结构·mysql·java-ee
黑蛋同志34 分钟前
array和linked list的区别
java
andrew_121940 分钟前
腾讯 IEG 游戏前沿技术 一面复盘
java·redis·sql·面试
寻求出路的程序媛1 小时前
JVM —— 类加载器的分类,双亲委派机制
java·jvm·面试
这孩子叫逆1 小时前
35. MyBatis中的缓存失效机制是如何工作的?
java·spring·mybatis
骆晨学长1 小时前
基于SpringBoot的校园失物招领系统
java·spring boot
汇匠源1 小时前
零工市场小程序:保障灵活就业
java·小程序·零工市场