基于开闭原则重构 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: Mapstring, 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作为抽象层依赖倒置。新增图表不需要修改ChartRenderer和ChartFactory工厂通过注册机制扩展。符合开闭原则系统对扩展开放可注册新图表对修改关闭核心流程不变。基于单一职责原则重构登录模块背景原登录模块一个类/函数承担了过多职责输入验证、用户认证、会话管理、日志记录、错误处理等导致代码臃肿、难以测试和维护。重构前反例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 MapString, 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 ListLoginSuccessHandler 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具体图表实现接口使用工厂注册表扩展新增图表无需修改原有类降低风险单一职责原则登录模块将输入校验、认证、会话、日志、附加动作分离到独立类提高可读性、可测试性降低耦合