面向对象编程:如何整合番茄钟的Counter类与Controls类?
如何整合Counter与Controls类实现番茄钟功能
嘿,我来帮你搞定这个类整合的问题~ 你已经把核心的倒计时和控制模块分开,这是很棒的OOP实践!针对你的需求,这里有几种常用且合理的整合方式,你可以根据项目复杂度来选:
方法1:依赖注入(最直接简单)
这种方式是把Counter实例直接传递给Controls,让Controls能直接调用Counter的方法、访问它的状态。好处是逻辑直观,适合小型项目。
修改Controls类:
class Controls extends Time { constructor(counterInstance) { super() this.counter = counterInstance // 保存Counter实例 this.session_btn = document.querySelectorAll('[data-session]') this.break_btn = document.querySelectorAll('[data-break]') } handleBreak(e) { const adjustment = parseInt(e.target.dataset.break); // 调用Counter的方法调整休息时长,比如假设Counter有adjustBreakTime方法 this.counter.adjustBreakTime(adjustment); console.log(`调整休息时长:${adjustment}分钟`); } handleSession(e) { const adjustment = parseInt(e.target.dataset.session); // 同理调用Counter的会话时长调整方法 this.counter.adjustSessionTime(adjustment); console.log(`调整工作时长:${adjustment}分钟`); } init() { // 注意:这里要绑定this,不然handleSession里的this会指向按钮元素 this.session_btn.forEach(btn => btn.addEventListener('click', this.handleSession.bind(this))); this.break_btn.forEach(btn => btn.addEventListener('click', this.handleBreak.bind(this))); } }
实例化代码:
const counter = new Counter(DOMnode); const controls = new Controls(counter); // 把Counter实例传进去 controls.init();
注意:别忘了在addEventListener时绑定this,不然事件处理函数里的this会指向触发事件的按钮,而不是Controls实例。
方法2:观察者模式(低耦合,适合扩展)
如果想让两个类之间的依赖更弱(比如以后可能要替换Counter或Controls的实现),可以用发布-订阅模式:Counter负责发布状态变化事件,Controls负责订阅并触发调整,或者Controls发布调整指令,Counter响应。
示例思路:
- 给Counter添加事件订阅/发布的方法:
class Counter { constructor(DOMnode) { this.DOMnode = DOMnode; this.listeners = {}; // 存储事件监听 } // 订阅事件 on(eventName, callback) { if (!this.listeners[eventName]) { this.listeners[eventName] = []; } this.listeners[eventName].push(callback); } // 发布事件 emit(eventName, data) { if (this.listeners[eventName]) { this.listeners[eventName].forEach(callback => callback(data)); } } // 调整休息时长的方法 adjustBreakTime(amount) { // 你的时长调整逻辑... this.emit('breakTimeChanged', this.currentBreakTime); // 发布状态变化事件 } }
- Controls类订阅事件,或者触发调整:
class Controls extends Time { constructor() { super(); this.session_btn = document.querySelectorAll('[data-session]'); this.break_btn = document.querySelectorAll('[data-break]'); } // 初始化时绑定Counter的事件 init(counterInstance) { this.counter = counterInstance; // 订阅Counter的状态变化事件 this.counter.on('breakTimeChanged', (newTime) => { console.log(`新的休息时长:${newTime}分钟`); // 这里可以更新UI显示 }); this.session_btn.forEach(btn => btn.addEventListener('click', (e) => { const adjustment = parseInt(e.target.dataset.session); this.counter.adjustSessionTime(adjustment); // 直接调用Counter方法 })); this.break_btn.forEach(btn => btn.addEventListener('click', (e) => { const adjustment = parseInt(e.target.dataset.break); this.counter.adjustBreakTime(adjustment); })); } }
- 实例化:
const counter = new Counter(DOMnode); const controls = new Controls(); controls.init(counter);
这种方式下,两个类之间的依赖非常松散,以后修改其中一个类的实现,只要保持事件接口一致,另一个类不需要大改。
方法3:组合模式(统一管理)
创建一个顶层的TomatoTimer类,把Counter和Controls作为它的子组件,由这个父类来协调两者的交互。这样两个子类之间完全不需要直接依赖,所有交互都通过父类中转。
class TomatoTimer { constructor(DOMnode) { this.counter = new Counter(DOMnode); this.controls = new Controls(); this.init(); } init() { // 给Controls绑定调整时长的方法,通过父类调用Counter this.controls.onSessionAdjust = (amount) => { this.counter.adjustSessionTime(amount); }; this.controls.onBreakAdjust = (amount) => { this.counter.adjustBreakTime(amount); }; this.controls.init(); } } // 修改Controls类,通过回调把调整指令传给父类 class Controls extends Time { constructor() { super(); this.session_btn = document.querySelectorAll('[data-session]'); this.break_btn = document.querySelectorAll('[data-break]'); this.onSessionAdjust = () => {}; // 默认空回调 this.onBreakAdjust = () => {}; } handleBreak(e) { const adjustment = parseInt(e.target.dataset.break); this.onBreakAdjust(adjustment); } handleSession(e) { const adjustment = parseInt(e.target.dataset.session); this.onSessionAdjust(adjustment); } init() { this.session_btn.forEach(btn => btn.addEventListener('click', this.handleSession.bind(this))); this.break_btn.forEach(btn => btn.addEventListener('click', this.handleBreak.bind(this))); } }
实例化就更简单了:
const tomatoTimer = new TomatoTimer(DOMnode);
这种方式适合把所有番茄钟的逻辑都集中在顶层类里,方便统一管理和维护。
内容的提问来源于stack exchange,提问作者Woah
相关产品推荐
相关产品推荐

