基于开闭原则重构 CRM 图表系统基于单一职责原则重构登录模块

基于开闭原则重构 CRM 图表系统基于单一职责原则重构登录模块

基于开闭原则重构 CRM 图表系统

背景

原 CRM 图表系统中,图表类型(柱状图、饼图、折线图等)的渲染逻辑耦合在一个大方法或类中,每增加一种图表类型就需要修改核心代码,违反开闭原则(对扩展开放,对修改关闭)。

重构前(反例)

typescript

// 反例:每增加图表类型都要修改 ChartRenderer class ChartRenderer { render(type: string, data: any) { if (type === 'bar') { // 渲染柱状图逻辑 console.log('渲染柱状图', data); } else if (type === 'pie') { // 渲染饼图逻辑 console.log('渲染饼图', data); } else if (type === 'line') { // 渲染折线图逻辑 console.log('渲染折线图', data); } // 新增图表 → 需要增加 else if,修改原有类 } }

重构后(符合开闭原则)

  • 定义抽象图表接口Chart

  • 每种图表实现该接口

  • 使用工厂或依赖注入管理图表实例

  • 新增图表只需新建实现类,无需修改已有代码

typescript

// 图表抽象接口 interface Chart { render(data: any): void; } // 柱状图实现 class BarChart implements Chart { render(data: any): void { console.log('渲染柱状图', data); } } // 饼图实现 class PieChart implements Chart { render(data: any): void { console.log('渲染饼图', data); } } // 折线图实现 class LineChart implements Chart { render(data: any): void { console.log('渲染折线图', data); } } // 新增雷达图(扩展开放) class RadarChart implements Chart { render(data: any): void { console.log('渲染雷达图', data); } } // 图表渲染器(对修改关闭) class ChartRenderer { private chart: Chart; constructor(chart: Chart) { this.chart = chart; } render(data: any): void { this.chart.render(data); } } // 可选:图表工厂,用于根据类型创建(但工厂模式本身也符合开闭原则,可通过配置映射避免修改) class ChartFactory { private static registry: Map<string, new () => Chart> = new Map(); static register(type: string, ctor: new () => Chart) { this.registry.set(type, ctor); } static create(type: string): Chart { const Ctor = this.registry.get(type); if (!Ctor) throw new Error(`未知图表类型: ${type}`); return new Ctor(); } } // 注册已知图表 ChartFactory.register('bar', BarChart); ChartFactory.register('pie', PieChart); ChartFactory.register('line', LineChart); // 新增图表只需调用 register('radar', RadarChart),不改动原有逻辑 // 使用示例 const barChart = ChartFactory.create('bar'); const renderer = new ChartRenderer(barChart); renderer.render({ sales: [100, 200] });

要点

  • 图表接口Chart作为抽象层,依赖倒置。

  • 新增图表不需要修改ChartRendererChartFactory(工厂通过注册机制扩展)。

  • 符合开闭原则:系统对扩展开放(可注册新图表),对修改关闭(核心流程不变)。


基于单一职责原则重构登录模块

背景

原登录模块一个类/函数承担了过多职责:输入验证、用户认证、会话管理、日志记录、错误处理等,导致代码臃肿、难以测试和维护。

重构前(反例)

java

// 反例:一个类承担了多个职责 public class LoginService { public boolean login(String username, String password) { // 1. 输入校验 if (username == null || username.trim().isEmpty()) { throw new IllegalArgumentException("用户名不能为空"); } if (password == null || password.length() < 6) { throw new IllegalArgumentException("密码长度不足"); } // 2. 数据库认证 User user = database.findUserByUsername(username); if (user == null || !user.getPassword().equals(encrypt(password))) { return false; } // 3. 创建会话 String sessionId = UUID.randomUUID().toString(); sessionStore.put(sessionId, user); // 4. 记录日志 logger.info("用户 " + username + " 登录成功,session: " + sessionId); // 5. 发送欢迎邮件(意外职责) emailService.sendWelcome(user.getEmail()); return true; } }

重构后(符合单一职责原则)

将不同职责分离到独立的类/模块中:

  • LoginValidator:负责输入校验

  • AuthenticationService:负责用户认证

  • SessionManager:负责会话创建和管理

  • LoginAuditLogger:负责日志记录

  • 可选:LoginSuccessHandler处理登录成功后的附加动作(如发送邮件)

java

// 1. 职责:输入校验 class LoginValidator { public void validate(String username, String password) { if (username == null || username.trim().isEmpty()) { throw new IllegalArgumentException("用户名不能为空"); } if (password == null || password.length() < 6) { throw new IllegalArgumentException("密码长度不足"); } } } // 2. 职责:用户认证 interface AuthenticationService { User authenticate(String username, String password) throws AuthException; } class DatabaseAuthenticationService implements AuthenticationService { private UserRepository userRepository; private PasswordEncoder passwordEncoder; @Override public User authenticate(String username, String password) { User user = userRepository.findByUsername(username); if (user == null || !passwordEncoder.matches(password, user.getPasswordHash())) { throw new AuthException("用户名或密码错误"); } return user; } } // 3. 职责:会话管理 class SessionManager { private Map<String, User> sessionStore = new ConcurrentHashMap<>(); public String createSession(User user) { String sessionId = UUID.randomUUID().toString(); sessionStore.put(sessionId, user); return sessionId; } // 可扩展其他会话操作:销毁、验证等 } // 4. 职责:登录日志记录 class LoginAuditLogger { public void logSuccess(String username, String sessionId) { LoggerFactory.getLogger(getClass()).info("用户 {} 登录成功,session: {}", username, sessionId); } public void logFailure(String username, String reason) { LoggerFactory.getLogger(getClass()).warn("用户 {} 登录失败: {}", username, reason); } } // 5. 可选:登录成功后的附加动作处理器(遵循开闭原则) interface LoginSuccessHandler { void onSuccess(User user, String sessionId); } class WelcomeEmailHandler implements LoginSuccessHandler { private EmailService emailService; @Override public void onSuccess(User user, String sessionId) { emailService.sendWelcome(user.getEmail()); } } // 最终组装:登录门面或协调者(只负责协调,不实现具体逻辑) class LoginFacade { private LoginValidator validator; private AuthenticationService authService; private SessionManager sessionManager; private LoginAuditLogger auditLogger; private List<LoginSuccessHandler> successHandlers; // 可以注入多个 public LoginResult login(String username, String password) { try { validator.validate(username, password); User user = authService.authenticate(username, password); String sessionId = sessionManager.createSession(user); auditLogger.logSuccess(username, sessionId); for (LoginSuccessHandler handler : successHandlers) { handler.onSuccess(user, sessionId); } return LoginResult.success(sessionId); } catch (IllegalArgumentException | AuthException e) { auditLogger.logFailure(username, e.getMessage()); return LoginResult.failure(e.getMessage()); } } }

要点

  • 每个类只负责一项明确的职责,变更原因只有一个。

  • LoginFacade仅做流程编排,不包含具体业务逻辑。

  • 认证方式(数据库、LDAP、OAuth)可通过AuthenticationService接口灵活替换。

  • 登录成功后的附加行为通过LoginSuccessHandler扩展,不修改核心流程,也符合开闭原则。


总结

原则重构对象核心手法收益
开闭原则CRM 图表系统定义抽象接口Chart,具体图表实现接口,使用工厂+注册表扩展新增图表无需修改原有类,降低风险
单一职责原则登录模块将输入校验、认证、会话、日志、附加动作分离到独立类提高可读性、可测试性,降低耦合