Java基础:面向对象设计原则详解【看这一篇就够了】

Java 面向对象设计原则是指导开发者编写高内聚、低耦合、易于维护和扩展的软件系统的准则。以下是其中的主要原则:

1. 单一职责原则 (SRP)

意味着一个类或者模块应当只有一个明确的责任或职责,即只做一件事。这样的设计使得类的功能单一、职责清晰,易于理解、测试和维护。当一个类承担了过多职责时,可能会导致复杂度增加,且任何职责的变化都可能影响到整个类,违反了“高内聚、低耦合”的原则。
示例
假设有一个 Employee 类,最初包含了员工的基本信息、工资计算、考勤管理等多个职责。按照 SRP,应将其拆分为多个类:

// 单独负责员工基本信息的类
class Employee {
    private String name;
    private String id;
    // ... 其他基本信息属性和对应的 getter/setter 方法
}

// 负责薪资计算的类
class SalaryCalculator {
    public double calculateSalary(Employee employee, List<AttendanceRecord> attendanceRecords) {
        // 根据员工信息和考勤记录计算工资
    }
}

// 负责考勤管理的类
class AttendanceManager {
    public void recordAttendance(Employee employee, Date date, boolean isPresent) {
        // 记录员工考勤
    }

    public List<AttendanceRecord> getAttendanceRecords(Employee employee) {
        // 获取员工考勤记录
    }
}

2. 开闭原则 (OCP)

提倡软件实体(如类、模块和函数)应对扩展开放,对修改关闭。这意味着在不修改现有代码的基础上,能够通过扩展来增加新功能或改变行为。通常通过抽象和多态(如使用接口或抽象类)来实现,具体实现通过派生新的子类或提供新的模块来扩展系统功能。
示例
考虑一个邮件发送服务,最初仅支持发送文本邮件。为了遵循 OCP,设计时使用接口和抽象类:

interface EmailService {
    void send(Email email);
}

class TextEmailService implements EmailService {
    @Override
    public void send(Email email) {
        // 发送文本邮件逻辑
    }
}

// 后续需要添加 HTML 邮件支持时,无需修改已有代码
class HtmlEmailService implements EmailService {
    @Override
    public void send(Email email) {
        // 发送 HTML 邮件逻辑
    }
}

// 使用方只需依赖 EmailService 接口,新增服务无需更改使用代码
class MailSender {
    private final EmailService emailService;

    public MailSender(EmailService service) {
        this.emailService = service;
    }

    public void sendEmail(Email email) {
        emailService.send(email);
    }
}

3. 里氏替换原则 (LSP)

子类应当可以替换其基类在任何地方出现,且不会导致程序逻辑的错误或异常行为。换句话说,任何使用基类的地方,都可以无缝地使用其子类对象,而不必关心具体的实现细节。这一原则强调了继承关系中,子类应当保持与基类的行为一致性。
示例
假设有一个 Shape 抽象类和两个子类 RectangleCircle。计算形状面积的接口在基类中定义,子类需确保遵守 LSP:

abstract class Shape {
    public abstract double getArea();
}

class Rectangle extends Shape {
    private double width, height;

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

    @Override
    public double getArea() {
        return width * height;
    }
}

class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

// 使用方可以使用 Shape 的任何子类,计算面积时无需关心具体类型
void printArea(Shape shape) {
    System.out.println("Area: " + shape.getArea());
}

4. 依赖倒置原则 (DIP)

两个关键点:
- 高层模块不应该依赖低层模块,两者都应该依赖于抽象(接口或抽象类);
- 抽象不应该依赖细节,细节(具体实现类)应该依赖于抽象。
示例
假设有一个 ReportGenerator 类需要持久化报告到数据库。遵循 DIP,不直接依赖具体数据库实现,而是依赖于抽象接口:

interface ReportRepository {
    void saveReport(Report report);
}

class FileReportRepository implements ReportRepository {
    @Override
    public void saveReport(Report report) {
        // 保存报告到文件系统的逻辑
    }
}

class DatabaseReportRepository implements ReportRepository {
    @Override
    public void saveReport(Report report) {
        // 保存报告到数据库的逻辑
    }
}

class ReportGenerator {
    private final ReportRepository repository;

    public ReportGenerator(ReportRepository repository) {
        this.repository = repository;
    }

    public void generateAndSaveReport(Data data) {
        Report report = generateReport(data);
        repository.saveReport(report);
    }

    private Report generateReport(Data data) {
        // 生成报告的逻辑
    }
}

// 使用时注入所需的 ReportRepository 实现
ReportGenerator generator = new ReportGenerator(new DatabaseReportRepository());
generator.generateAndSaveReport(someData);

5. 接口隔离原则 (ISP)

客户端(使用者)不应该被迫依赖于它不需要的接口方法。也就是说,一个庞大的接口应该拆分成多个更小的、更具体的接口,每个接口只包含客户端真正需要的方法。这样可以减少不必要的耦合,提高接口的使用灵活性,防止因接口方法变动对无关客户端造成影响。
示例
假设有一个 ImageEditor 接口,包含了多种图像处理操作。为了遵循 ISP,可以将其拆分为更细粒度的接口:

interface Resizeable {
    void resize(int newWidth, int newHeight);
}

interface Filterable {
    void applyFilter(Filter filter);
}

class AdvancedImageEditor implements Resizeable, Filterable {
    // 实现 Resizeable 和 Filterable 接口的方法
}

// 使用方仅依赖所需功能的接口
class ThumbnailGenerator {
    private final Resizeable imageEditor;

    public ThumbnailGenerator(Resizeable editor) {
        this.imageEditor = editor;
    }

    public void createThumbnail(Image image) {
        imageEditor.resize(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
    }
}

class FilterApplicator {
    private final Filterable imageEditor;

    public FilterApplicator(Filterable editor) {
        this.imageEditor = editor;
    }

    public void applyGrayscaleFilter(Image image) {
        imageEditor.applyFilter(new GrayscaleFilter());
    }
}

6. 迪米特法则 (LOD)

一个对象应当对其它对象有最少的了解,只和直接的朋友(直接交互的对象)交流。这一原则限制了对象之间的交互深度,降低了系统的耦合度,使系统更易于理解和维护。遵循迪米特法则的设计通常会采用中间件、代理模式等来限制对象之间的直接访问。
示例
考虑一个 Order 类和 Customer 类。如果 Order 直接访问 Customer 的详细信息(如地址、联系方式等),则违反了 LOD。为遵循此原则,可以引入中间类:

class Customer {
    private Address address;
    private ContactInfo contactInfo;

    // ... 其他属性和方法
}

class Order {
    private OrderDeliveryDetails deliveryDetails;

    public void setDeliveryDetails(OrderDeliveryDetails details) {
        this.deliveryDetails = details;
    }

    // ... 其他属性和方法
}

class OrderDeliveryDetails {
    private Address deliveryAddress;
    private ContactInfo deliveryContact;

    // ... 构造函数、getter/setter 和其他方法

    // OrderDeliveryDetails 可以从 Customer 中获取必要信息,而非 Order 直接访问 Customer
    public void populateFromCustomer(Customer customer) {
        deliveryAddress = customer.getAddress();
        deliveryContact = customer.getContactInfo();
    }
}

// 使用时,Order 仅与 OrderDeliveryDetails 交互,减少了与 Customer 的直接耦合
Customer customer = new Customer(...);
Order order = new Order();
OrderDeliveryDetails details = new OrderDeliveryDetails();
details.populateFromCustomer(customer);
order.setDeliveryDetails(details);

这些原则并不是孤立的,它们相互关联、互为补充,共同指导开发者编写出具有良好结构、易于维护和扩展的面向对象软件。在实际开发过程中,需要根据具体情况灵活运用这些原则,以达到最佳的设计效果。通过这些原则指导的设计能够提高代码的可读性、可维护性、可扩展性和降低模块间的耦合度。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值