MST

星途 面试题库

面试题:Java复杂系统中SOLID原则与设计模式的权衡及优化

假设你正在参与一个大型Java企业级应用系统的重构,该系统之前的设计没有严格遵循SOLID原则,导致代码耦合度高、维护困难。请详细阐述你将如何逐步引入SOLID原则,并结合具体的设计模式(如策略模式、装饰器模式等)进行重构。在重构过程中,如何权衡不同设计模式对系统性能、资源占用等方面的影响,以及如何通过优化策略将负面影响降到最低?
43.8万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 引入SOLID原则步骤

单一职责原则(SRP)

  • 分析与拆分:审查现有类,确定每个类所承担的多个职责。例如,一个类既处理用户认证逻辑,又负责订单处理。将其拆分为UserAuthentication类专注于认证,OrderProcessor类负责订单相关操作。
  • 代码重构:把相关方法和数据从原类迁移到新类,确保每个类仅有一个明确的职责。

开闭原则(OCP)

  • 抽象与扩展:识别可能变化的部分,通过抽象(如接口或抽象类)进行封装。以电商系统的促销策略为例,定义PromotionStrategy接口,包含calculateDiscount方法。
  • 实现具体策略:针对不同促销策略(如满减、折扣)创建实现该接口的具体类,如FullReductionPromotionDiscountPromotion。这样在新增促销策略时,无需修改原有代码,只需创建新的实现类。

里氏替换原则(LSP)

  • 确保继承正确性:检查继承关系,确保子类可以完全替换父类。比如Square类继承Rectangle类,需保证Square对象在任何使用Rectangle对象的地方都能正常工作。如果Rectangle有设置宽和高的方法,Square在重写时要保证设置宽高的行为符合Square的定义(即宽高相等)。
  • 修正违反情况:若发现子类行为与父类不一致,考虑重新设计继承结构,如使用组合代替继承。

接口隔离原则(ISP)

  • 拆分臃肿接口:找出包含过多方法的接口,将其拆分为多个专用接口。例如,一个Employee接口包含workmanagereport等方法,对于普通员工和经理可能不需要全部方法。可以拆分为Worker接口(包含work方法)、Manager接口(包含managereport方法)。
  • 类实现专用接口:让类只实现其需要的接口,减少不必要的依赖。

依赖倒置原则(DIP)

  • 依赖抽象而非具体:在高层模块中,依赖抽象类或接口,而不是具体实现类。例如,一个PaymentService类依赖PaymentGateway接口,而不是具体的AlipayGatewayWeChatPayGateway类。
  • 通过构造函数或方法注入:在创建PaymentService实例时,通过构造函数或方法参数传入具体的PaymentGateway实现类,实现依赖注入。

2. 结合设计模式重构

策略模式

  • 应用场景:用于解决系统中算法多变的问题,如上述电商系统的促销策略。
  • 实现方式:定义策略接口,创建具体策略类实现接口,在需要使用策略的地方注入具体策略对象。例如:
// 策略接口
public interface PromotionStrategy {
    double calculateDiscount(double price);
}

// 具体策略类1
public class FullReductionPromotion implements PromotionStrategy {
    @Override
    public double calculateDiscount(double price) {
        // 满减逻辑
        return price > 100? price - 20 : price;
    }
}

// 具体策略类2
public class DiscountPromotion implements PromotionStrategy {
    @Override
    public double calculateDiscount(double price) {
        // 折扣逻辑
        return price * 0.8;
    }
}

// 使用策略的类
public class Order {
    private PromotionStrategy promotionStrategy;

    public Order(PromotionStrategy promotionStrategy) {
        this.promotionStrategy = promotionStrategy;
    }

    public double calculateTotalPrice(double originalPrice) {
        return promotionStrategy.calculateDiscount(originalPrice);
    }
}

装饰器模式

  • 应用场景:当需要在运行时给对象添加新功能时使用。例如,给Logger类添加加密功能。
  • 实现方式:定义抽象组件(如Logger接口),具体组件类(如FileLogger实现Logger接口),抽象装饰器类继承抽象组件类并持有抽象组件的引用,具体装饰器类继承抽象装饰器类并实现新增功能。例如:
// 抽象组件
public interface Logger {
    void log(String message);
}

// 具体组件
public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("Logging to file: " + message);
    }
}

// 抽象装饰器
public abstract class LoggerDecorator implements Logger {
    protected Logger logger;

    public LoggerDecorator(Logger logger) {
        this.logger = logger;
    }

    @Override
    public void log(String message) {
        logger.log(message);
    }
}

// 具体装饰器
public class EncryptedLoggerDecorator extends LoggerDecorator {
    public EncryptedLoggerDecorator(Logger logger) {
        super(logger);
    }

    @Override
    public void log(String message) {
        String encryptedMessage = encrypt(message);
        super.log(encryptedMessage);
    }

    private String encrypt(String message) {
        // 加密逻辑
        return new StringBuilder(message).reverse().toString();
    }
}

3. 权衡设计模式对系统的影响及优化策略

性能影响及优化

  • 策略模式:每次切换策略可能会创建新的策略对象,增加对象创建开销。优化策略是使用策略对象池,复用已创建的策略对象,减少对象创建频率。
  • 装饰器模式:多层装饰可能导致方法调用链变长,影响性能。可以通过缓存装饰后的结果,避免重复计算。例如,在EncryptedLoggerDecorator中缓存加密后的消息。

资源占用影响及优化

  • 策略模式:策略对象池会占用一定内存空间。可以设置对象池的最大容量,当超过容量时,采用合适的淘汰算法(如LRU)释放不再使用的策略对象。
  • 装饰器模式:过多的装饰器类会增加代码体积和内存占用。合理规划装饰器结构,避免不必要的装饰,对于不常用的装饰功能,可以采用动态加载的方式,减少初始内存占用。