原创

java面试题-面向对象设计原则有哪些?

标题:深入了解面向对象设计原则及其在Java中的实践

概述

在面向对象编程中,设计原则是指导我们创建可维护、灵活和可扩展的软件的准则。本文将介绍面向对象设计的七大原则,包括单一职责原则、开闭原则、里氏替代原则、依赖注入原则、接口分离原则、迪米特原则和组合/聚合复用原则,并提供在Java中应用这些原则的示例代码。

单一职责原则 (Single Responsibility Principle - SRP)

单一职责原则要求一个类只负责一个功能领域中的相应职责。这有助于降低类的复杂性,提高代码的可维护性。

示例代码

class Book {
    String title;
    String author;

    Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
}

解释: 在这个示例中,Book 类只负责表示书籍的基本信息,符合单一职责原则。


开闭原则 (Open-Closed Principle - OCP)

开闭原则规定软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码的情况下,可以通过添加新功能来扩展系统。

示例代码

interface BookFilter {
    boolean isSatisfied(Book book);
}

class BookFilterByAuthor implements BookFilter {
    private String author;

    BookFilterByAuthor(String author) {
        this.author = author;
    }

    @Override
    public boolean isSatisfied(Book book) {
        return book.author.equals(author);
    }
}

class BookFilterByTitle implements BookFilter {
    private String title;

    BookFilterByTitle(String title) {
        this.title = title;
    }

    @Override
    public boolean isSatisfied(Book book) {
        return book.title.equals(title);
    }
}

解释: 在这个示例中,通过接口 BookFilter 描述了书籍过滤的规范。BookFilterByAuthorBookFilterByTitle 是对该规范的不同实现,通过添加新的过滤器类来扩展系统而不需要修改现有代码。


里氏替代原则 (Liskov Substitution Principle - LSP)

里氏替代原则要求子类能够替代其基类而不改变程序的正确性。这意味着在使用继承时,子类应该保留其父类的行为,同时可以扩展或重写父类的方法。

示例代码

class Rectangle {
    protected int width;
    protected int height;

    Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    int getArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    Square(int side) {
        super(side, side);
    }

    // 重写 getArea 方法
    @Override
    int getArea() {
        return width * width;
    }
}

解释: 在这个示例中,Square 类继承自 Rectangle 类,但重写了 getArea 方法。这违反了里氏替代原则,因为使用 Square 替代 Rectangle 可能导致不符合预期的结果。

依赖注入原则 (Dependency Inversion Principle - DIP)

依赖注入原则要求高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

示例代码

// 高层模块
class BookReader {
    private Book book;

    // 通过构造函数注入依赖
    BookReader(Book book) {
        this.book = book;
    }

    void readBook() {
        System.out.println("Reading book: " + book.title);
        // 其他操作...
    }
}

// 低层模块
class Book {
    String title;
    String author;

    Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
}

解释: 在这个示例中,BookReader 高层模块通过构造函数注入了 Book 低层模块的依赖。这遵循了依赖注入原则,使得高层模块不依赖于具体的 Book 类,而是依赖于抽象的 Book


接口分离原则 (Interface Segregation Principle - ISP)

接口分离原则要求一个类对另一个类的依赖应该建立在最小的接口上。换句话说,不应该强迫类依赖于它们不使用的接口。

示例代码

// 不好的设计
interface Worker {
    void work();
    void eat();
}

class Engineer implements Worker {
    @Override
    public void work() {
        System.out.println("Engineer is working");
    }

    @Override
    public void eat() {
        System.out.println("Engineer is eating");
    }
}

class Manager implements Worker {
    @Override
    public void work() {
        System.out.println("Manager is working");
    }

    @Override
    public void eat() {
        System.out.println("Manager is eating");
    }
}

解释: 在这个示例中,Worker 接口包含了 workeat 两个方法,但在实际使用中,EngineerManager 类并不需要同时具备这两个行为。更好的设计是将接口分离为 WorkableEatable 两个接口,使得每个类只需实现其需要的接口。

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Engineer implements Workable, Eatable {
    @Override
    public void work() {
        System.out.println("Engineer is working");
    }

    @Override
    public void eat() {
        System.out.println("Engineer is eating");
    }
}

class Manager implements Workable {
    @Override
    public void work() {
        System.out.println("Manager is working");
    }
}

迪米特原则 (Law of Demeter - LoD)

迪米特原则要求一个对象应该对其他对象保持最少的了解,即一个类对于自己依赖的类知道的越少越好。

示例代码

// 不好的设计
class Mediator {
    void mediate(Colleague colleague1, Colleague colleague2) {
        // 中介者直接操作 colleague1 和 colleague2 的细节
    }
}

class Colleague {
    void doSomething() {
        // 具体操作
    }
}

解释: 在这个示例中,Mediator 直接操作 Colleague 的细节,违反了迪米特原则。更好的设计是通过让 Colleague 自己完成具体的操作,然后由 Mediator 协调它们之间的交互。

class Mediator {
    void mediate(Colleague colleague1, Colleague colleague2) {
        // 中介者协调 colleague1 和 colleague2 之间的交互
        colleague1.doSomething();
        colleague2.doSomething();
    }
}

class Colleague {
    void doSomething() {
        // 具体操作
    }
}

组合/聚合复用原则 (Composite/Aggregate Reuse Principle - CARP)

组合/聚合复用原则要求尽量使用组合和聚合,而不是使用继承来达到复用的目的。这有助于降低类与类之间的耦合性。

示例代码

// 不好的设计
class Engine {
    void start() {
        // 启动引擎
    }
}

class Car extends Engine {
    void drive() {
        start(); // 继承自 Engine
        // 具体驾驶操作
    }
}

解释: 在这个示例中,Car

继承了 Engine 类,这种设计增加了类之间的耦合性。更好的设计是使用组合,将 Engine 对象作为 Car 的成员。

class Engine {
    void start() {
        // 启动引擎
    }
}

class Car {
    private Engine engine;

    Car(Engine engine) {
        this.engine = engine;
    }

    void drive() {
        engine.start(); // 使用组合
        // 具体驾驶操作
    }
}

总结

以上原则是面向对象设计的基本原则,它们帮助开发人员编写可维护、灵活且可扩展的代码。在实际开发中,了解并应用这些原则将有助于创建高质量、易读且易维护的面向对象程序。

正文到此结束
本文目录