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
描述了书籍过滤的规范。BookFilterByAuthor
和 BookFilterByTitle
是对该规范的不同实现,通过添加新的过滤器类来扩展系统而不需要修改现有代码。
里氏替代原则 (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
接口包含了 work
和 eat
两个方法,但在实际使用中,Engineer
和 Manager
类并不需要同时具备这两个行为。更好的设计是将接口分离为 Workable
和 Eatable
两个接口,使得每个类只需实现其需要的接口。
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(); // 使用组合
// 具体驾驶操作
}
}
总结
以上原则是面向对象设计的基本原则,它们帮助开发人员编写可维护、灵活且可扩展的代码。在实际开发中,了解并应用这些原则将有助于创建高质量、易读且易维护的面向对象程序。
- 本文标签: Java 面试题
- 本文链接: https://www.jietongc.com/article/40
- 版权声明: 本文由大熊科技原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权