面向对象的设计原则

面向对象的设计原则是编写可维护、可扩展、可复用软件的基础。以下是主要的面向对象设计原则:

1. SOLID 原则

1.1 单一职责原则 (SRP)

一个类应该只有一个引起变化的原因

// 违反 SRP
class User {
    private String name;
    private String email;
    
    public void saveToDatabase() {
        // 数据库操作
    }
    
    public void sendEmail() {
        // 发送邮件
    }
    
    public void validate() {
        // 验证逻辑
    }
}

// 遵循 SRP
class User {
    private String name;
    private String email;
    
    // 只包含用户核心属性
}

class UserRepository {
    public void save(User user) {
        // 数据库操作
    }
}

class EmailService {
    public void sendEmail(User user) {
        // 发送邮件
    }
}

class UserValidator {
    public boolean validate(User user) {
        // 验证逻辑
    }
}

1.2 开闭原则 (OCP)

对扩展开放,对修改关闭

// 违反 OCP
class ShapeDrawer {
    public void draw(String shapeType) {
        if (shapeType.equals("circle")) {
            drawCircle();
        } else if (shapeType.equals("rectangle")) {
            drawRectangle();
        }
        // 添加新形状需要修改此类
    }
}

// 遵循 OCP
interface Shape {
    void draw();
}

class Circle implements Shape {
    @Override
    public void draw() {
        // 绘制圆形
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        // 绘制矩形
    }
}

class ShapeDrawer {
    public void draw(Shape shape) {
        shape.draw(); // 无需修改即可支持新形状
    }
}

// 扩展新形状
class Triangle implements Shape {
    @Override
    public void draw() {
        // 绘制三角形
    }
}

1.3 里氏替换原则 (LSP)

子类必须能够替换它们的父类

// 违反 LSP
class Rectangle {
    protected int width;
    protected int height;
    
    public void setWidth(int width) {
        this.width = width;
    }
    
    public void setHeight(int height) {
        this.height = height;
    }
    
    public int getArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width); // 破坏了矩形行为
    }
    
    @Override
    public void setHeight(int height) {
        super.setHeight(height);
        super.setWidth(height); // 破坏了矩形行为
    }
}

// 测试代码
void testArea(Rectangle rectangle) {
    rectangle.setWidth(5);
    rectangle.setHeight(4);
    assert rectangle.getArea() == 20; // 对于 Square 会失败
}

// 遵循 LSP
abstract class Shape {
    public abstract int getArea();
}

class Rectangle extends Shape {
    private int width;
    private int height;
    
    // getters and setters
    
    @Override
    public int getArea() {
        return width * height;
    }
}

class Square extends Shape {
    private int side;
    
    public void setSide(int side) {
        this.side = side;
    }
    
    @Override
    public int getArea() {
        return side * side;
    }
}

1.4 接口隔离原则 (ISP)

客户端不应该被迫依赖它们不使用的接口

// 违反 ISP
interface Worker {
    void work();
    void eat();
    void sleep();
}

class HumanWorker implements Worker {
    @Override
    public void work() { /* 工作 */ }
    
    @Override
    public void eat() { /* 吃饭 */ }
    
    @Override
    public void sleep() { /* 睡觉 */ }
}

class RobotWorker implements Worker {
    @Override
    public void work() { /* 工作 */ }
    
    @Override
    public void eat() { 
        throw new UnsupportedOperationException("机器人不需要吃饭");
    }
    
    @Override
    public void sleep() {
        throw new UnsupportedOperationException("机器人不需要睡觉");
    }
}

// 遵循 ISP
interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

interface Sleepable {
    void sleep();
}

class HumanWorker implements Workable, Eatable, Sleepable {
    @Override
    public void work() { /* 工作 */ }
    
    @Override
    public void eat() { /* 吃饭 */ }
    
    @Override
    public void sleep() { /* 睡觉 */ }
}

class RobotWorker implements Workable {
    @Override
    public void work() { /* 工作 */ }
}

1.5 依赖倒置原则 (DIP)

依赖抽象而不是具体实现

// 违反 DIP
class LightBulb {
    public void turnOn() {
        // 开灯
    }
    
    public void turnOff() {
        // 关灯
    }
}

class Switch {
    private LightBulb bulb;
    
    public Switch() {
        this.bulb = new LightBulb(); // 依赖具体类
    }
    
    public void operate() {
        // 操作灯泡
        bulb.turnOn();
    }
}

// 遵循 DIP
interface Switchable {
    void turnOn();
    void turnOff();
}

class LightBulb implements Switchable {
    @Override
    public void turnOn() {
        // 开灯
    }
    
    @Override
    public void turnOff() {
        // 关灯
    }
}

class Fan implements Switchable {
    @Override
    public void turnOn() {
        // 开风扇
    }
    
    @Override
    public void turnOff() {
        // 关风扇
    }
}

class Switch {
    private Switchable device;
    
    public Switch(Switchable device) { // 依赖抽象
        this.device = device;
    }
    
    public void operate() {
        device.turnOn();
    }
}

2. 其他重要原则

2.1 组合优于继承原则

优先使用组合而不是继承来复用代码

// 使用继承(不推荐)
class Bird {
    public void fly() {
        // 飞行逻辑
    }
}

class Penguin extends Bird {
    // 企鹅不会飞,但继承了 fly 方法
    @Override
    public void fly() {
        throw new UnsupportedOperationException("企鹅不会飞");
    }
}

// 使用组合(推荐)
interface Flyable {
    void fly();
}

class FlyingAbility implements Flyable {
    @Override
    public void fly() {
        // 飞行逻辑
    }
}

class Bird {
    protected Flyable flyAbility;
    
    public Bird(Flyable flyAbility) {
        this.flyAbility = flyAbility;
    }
    
    public void performFly() {
        if (flyAbility != null) {
            flyAbility.fly();
        }
    }
}

class Sparrow extends Bird {
    public Sparrow() {
        super(new FlyingAbility());
    }
}

class Penguin extends Bird {
    public Penguin() {
        super(null); // 没有飞行能力
    }
}

2.2 迪米特法则(最少知识原则)

一个对象应该对其他对象有最少的了解

// 违反迪米特法则
class Customer {
    private Wallet wallet;
    
    public Wallet getWallet() {
        return wallet;
    }
}

class Paperboy {
    public void charge(Customer customer, int amount) {
        Wallet wallet = customer.getWallet(); // 知道 Customer 有 Wallet
        if (wallet.getMoney() >= amount) {
            wallet.subtractMoney(amount);
        }
    }
}

// 遵循迪米特法则
class Customer {
    private Wallet wallet;
    
    public boolean pay(int amount) {
        return wallet.subtractMoneyIfEnough(amount);
    }
}

class Paperboy {
    public void charge(Customer customer, int amount) {
        customer.pay(amount); // 只与 Customer 交互
    }
}

2.3 不要重复你自己 (DRY)

避免代码重复

// 违反 DRY
class UserService {
    public void createUser(String username, String email) {
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("邮箱格式错误");
        }
        // 创建用户逻辑
    }
    
    public void updateUser(String username, String email) {
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("邮箱格式错误");
        }
        // 更新用户逻辑
    }
}

// 遵循 DRY
class Validator {
    public static void validateUsername(String username) {
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
    }
    
    public static void validateEmail(String email) {
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("邮箱格式错误");
        }
    }
}

class UserService {
    public void createUser(String username, String email) {
        Validator.validateUsername(username);
        Validator.validateEmail(email);
        // 创建用户逻辑
    }
    
    public void updateUser(String username, String email) {
        Validator.validateUsername(username);
        Validator.validateEmail(email);
        // 更新用户逻辑
    }
}

3. 实际应用示例

3.1 电商系统设计

// 遵循 SOLID 原则的电商系统
interface PaymentMethod {
    void pay(double amount);
}

class CreditCardPayment implements PaymentMethod {
    @Override
    public void pay(double amount) {
        // 信用卡支付逻辑
    }
}

class PayPalPayment implements PaymentMethod {
    @Override
    public void pay(double amount) {
        // PayPal 支付逻辑
    }
}

interface NotificationService {
    void sendNotification(String message);
}

class EmailNotification implements NotificationService {
    @Override
    public void sendNotification(String message) {
        // 邮件通知逻辑
    }
}

class SMSNotification implements NotificationService {
    @Override
    public void sendNotification(String message) {
        // 短信通知逻辑
    }
}

class OrderProcessor {
    private PaymentMethod paymentMethod;
    private NotificationService notificationService;
    
    public OrderProcessor(PaymentMethod paymentMethod, 
                         NotificationService notificationService) {
        this.paymentMethod = paymentMethod;
        this.notificationService = notificationService;
    }
    
    public void processOrder(Order order) {
        paymentMethod.pay(order.getTotalAmount());
        notificationService.sendNotification("订单处理完成");
    }
}

3.2 设计原则检查清单

原则 检查问题
SRP 这个类是否只有一个职责?
OCP 添加新功能是否需要修改现有代码?
LSP 子类是否能完全替换父类?
ISP 接口是否足够小和专注?
DIP 是否依赖抽象而不是具体实现?
组合 是否过度使用继承?
迪米特 对象是否知道太多其他对象?
DRY 是否有重复的代码逻辑?

4. 设计原则的权衡

在实际项目中,需要根据具体情况权衡这些原则:

  1. 过度设计 vs 适度设计:不要为了原则而原则
  2. 项目阶段考虑:原型阶段可以适当放宽,核心系统要严格遵循
  3. 团队技能:根据团队经验选择合适的设计复杂度
  4. 性能要求:某些原则可能带来性能开销
// 适度设计的例子
// 对于小型项目,可以适当合并职责
class SimpleUserService {
    // 合并了用户相关的多个职责,但代码更简单
    public void register(User user) { }
    public void login(String username, String password) { }
    public void resetPassword(String email) { }
}

// 对于大型企业系统,应该严格分离
interface UserRegistration { }
interface UserAuthentication { }
interface PasswordManagement { }

掌握这些设计原则能够帮助你创建更加健壮、可维护的面向对象系统。关键在于理解原则背后的思想,而不是机械地套用。

posted @ 2025-10-13 21:09  阿木隆1237  阅读(11)  评论(0)    收藏  举报