1. 从一个需求说起
左边是一个输入框,右边是输出结果(只读)。现在要求点击读取
按钮把左边文本框的内容读取到右边结果栏。
同时在读取的过程中,可以做一些操作,例如:
- 设置字体颜色
- 添加背景和边框
- 添加序号
- …
基础功能代码(只把左边的值读取到右边)如下:
// 省略了css和head代码 <body> <div class="box"> <div class="left"> <textarea rows="15"></textarea> </div> <div class="middle"> <button>读取</button> </div> <div class="right"> <div class="result"> </div> </div> </div>
<script> class NormalReader { constructor (txt) { this.txt = txt; this.buffer = txt.split(/[\n\r]/g); this.index = 0; } readLine() { if (this.index > this.buffer.length - 1) { return; } return this.buffer[this.index++]; } } let btn = document.querySelector('button'); let result = document.querySelector('.result');
btn.addEventListener('click', e => { const txt = document.querySelector('textarea').value; const reader = new NormalReader(txt); result.innerHTML = "";
let line; while (line = reader.readLine()) { const div = document.createElement('div'); div.innerHTML = line; result.append(div); } }); </script> </body>
|
2. 用继承的方式实现以上功能
- 设置字体颜色
class ColorReader extends NormalReader { readLine() { let line = super.readLine(); if (!line) return; const span = document.createElement('span'); span.style.color = 'red'; span.innerHTML = line; return span.outerHTML; } }
let btn = document.querySelector('button'); let result = document.querySelector('.result');
btn.addEventListener('click', e => { const txt = document.querySelector('textarea').value; const reader = new ColorReader(txt); result.innerHTML = "";
let line; while (line = reader.readLine()) { const div = document.createElement('div'); div.innerHTML = line; result.append(div); } });
|
- 添加背景和边框
class BoxReader extends ColorReader { readLine() { let line = super.readLine(); if (!line) return; const div = document.createElement('div'); div.style.border = '1px solid #999'; div.style.background = 'green'; div.style.height = '25px'; div.innerHTML = line; return div.outerHTML; } } btn.addEventListener('click', e => { const txt = document.querySelector('textarea').value; const reader = new BoxReader(txt); });
|
- 添加序号
class OrderedReader extends BoxReader { constructor(txt) { super(txt); this.num = 1; } readLine() { let line = super.readLine(); if (!line) return; return `${this.num++} ${line}`; } } btn.addEventListener('click', e => { const txt = document.querySelector('textarea').value; const reader = new OrderedReader(txt); });
|
缺点:
用继承的方式,能实现这些功能,有个缺点,就是不方便组合这些功能,比如只想要添加序号 和 改变字体颜色这两个功能,那么需要修改OrderedReader的父类。
3. 装饰器模式
- 首先添加一个Decorator类
构造它是可以接受一个reader类,然后它提供readLine方法,它会调用reader的readLine方法。
其他实现具体功能的reader可以继承这个类,从而可以调用其他reader的方法。
class Decorator { constructor(reader) { this.reader = reader; } readLine() { return this.reader.readLine(); } }
|
- 改造其他reader类 (很小的改动,只需要修改继承的类名即可)
class ColorReader extends Decorator { readLine() { let line = super.readLine(); if (!line) return; const span = document.createElement('span'); span.style.color = 'red'; span.innerHTML = line; return span.outerHTML; } } class BoxReader extends Decorator { readLine() { let line = super.readLine(); if (!line) return; const div = document.createElement('div'); div.style.border = '1px solid #999'; div.style.background = 'green'; div.style.height = '25px'; div.innerHTML = line; return div.outerHTML; } } class OrderedReader extends Decorator { constructor(txt) { super(txt); this.num = 1; } readLine() { let line = super.readLine(); if (!line) return; return `${this.num++} - ${line}`; } }
|
- 修改构造reader的方法,可以方便的组合各种功能
btn.addEventListener('click', e => { const txt = document.querySelector('textarea').value; const reader = new ColorReader(new OrderedReader(new NormalReader(txt))); });
|
结果:
4. 完整代码
<html> <head> <title>Test decorator design pattern</title> <style> .box { width: 500px; display: flex; justify-content: space-between; align-items: center; }
.result { border: 1px solid; width: 200px; height: 200px; } </style> </head> <body> <h2>学习装饰器模式</h2> <div class="box"> <div class="left"> <textarea rows="15"></textarea> </div> <div class="middle"> <button>读取</button> </div> <div class="right"> <div class="result"> </div> </div> </div>
<br> <div> 总结: <ul> <li>继承:以继承的方式来实现,添加序号,设置字体颜色,添加边框,继承得到的功能都是静态的,无法动态组合各个功能点</li> <li>装饰器模式:可以很好的组合各个功能,比如设置字体颜色和添加边框</li> </ul> </div>
<script> class NormalReader { constructor (txt) { this.txt = txt; this.buffer = txt.split(/[\n\r]/g); this.index = 0; } readLine() { console.log(this.buffer); if (this.index > this.buffer.length - 1) { return; } return this.buffer[this.index++]; } }
class Decorator { constructor(reader) { this.reader = reader; } readLine() { return this.reader.readLine(); } }
class ColorReader extends Decorator { readLine() { let line = super.readLine(); if (!line) return; const span = document.createElement('span'); span.style.color = 'red'; span.innerHTML = line; return span.outerHTML; } } class BoxReader extends Decorator { readLine() { let line = super.readLine(); if (!line) return; const div = document.createElement('div'); div.style.border = '1px solid #999'; div.style.background = 'green'; div.style.height = '25px'; div.innerHTML = line; return div.outerHTML; } } class OrderedReader extends Decorator { constructor(txt) { super(txt); this.num = 1; } readLine() { let line = super.readLine(); if (!line) return;
return `${this.num++} - ${line}`; } }
let btn = document.querySelector('button'); let result = document.querySelector('.result');
btn.addEventListener('click', e => { const txt = document.querySelector('textarea').value; const reader = new ColorReader(new OrderedReader(new NormalReader(txt)));
result.innerHTML = "";
let line; while (line = reader.readLine()) { const div = document.createElement('div'); div.innerHTML = line; result.append(div); } }); </script> </body> </html>
|
(说明:本文是学习了抖音大神的讲解视频,动手写并总结整理而成。少数代码细节可能有细微差别。通过实际案例,学习设计模式,特别清楚。分享出来,希望更多人能学习和掌握设计模式。)