前言:设计模式是程序员进阶的必经之路,真正地理解、掌握设计模式对编写高质量代码是极有帮助的,下面简单谈一下自己对设计模式的理解。
概括:设计模式就是代码书写经验,为了应对各种场景,经过前人不断的总结,压缩,形成的一套又一套的代码的书写规范,于是就形成了设计模式。
一、单例模式
单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
代码如下:
// 给页面创建一个信息框
// 每次执行,设置新内容
// 信息框只有一个,只是内容在改
function Msg() {
this.ele = document.createElement("div");
document.body.appendChild(this.ele);
}
Msg.prototype.init = function (str) {
this.ele.innerHTML = str;
}
var f = (function () {
var instance;
return function (text) {
if (!instance) {
instance = new Msg();
}
instance.init(text);
return instance;
}
})()
var m1 = f("hello");
var m2 = f("world");
console.log(m1 === m2); //true
二、组合模式
组合模式是一种专为创建Web.上的动态用户界面而量身定制的模式。使用这种模式,可
以用一条命令在多个对象上激发复杂的或递归的行为。这可以简化粘合性代码,使其
更容易维护,而那些复杂行为则被委托给各个对象。
代码如下:
class Game {
init() {
console.log("打游戏");
}
}
class Run {
init() {
console.log("跑步");
}
}
class Study {
init() {
console.log("学习");
}
}
// 组合器,用来组合功能
class Comb {
constructor() {
this.skills = [];
}
// 用来要组合的功能,接收组合的对象
add(task) {
this.skills.push(task);
}
// 用来批量执行的功能
action() {
this.skills.forEach(val => {
val.init();
})
}
}
// 创建一个组合器
var f = new Comb();
// 提前将,将来要批量操作的对象,组合起来
f.add(new Game());
f.add(new Run());
f.add(new Study());
// 等待何时的时机,执行组合器的启动功能
f.action();
三、观察者模式:也称发布订阅者模式
在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式是一种管理人与其任务之间的关系(确切地讲,是对象及其行为和状态之间的关系)的得力工具。用JavaScript的话来说,这种模式的实质就是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知。
简而言之:
发布者:发布信息,会随时新自身的信息或状态;
订阅者:接收信息,接收到发布者发布的信息,从而做出对应的改变或执行;
下面是一个简单的广播通信,实现一对多的对应关系:
function Stu(n) {
this.name = n;
this.type = function () {
if (Math.random() > 0.5) {
return "学习";
} else {
return "打游戏";
}
}
}
function Teacher(n) {
this.name = n;
this.listen = function (t) {
if (t == "学习") {
console.log("好孩子");
} else {
console.log("坏孩子");
}
}
}
function Teacher2(n) {
this.name = n;
this.listen = function (t) {
if (t == "学习") {
console.log("奖励");
} else {
console.log("惩罚");
}
}
}
var s = new Stu("张三");
var t = s.type();
console.log(t);
var t1 = new Teacher("老师1");
t1.listen(t);
var t2 = new Teacher2("老师2");
t2.listen(t);
四、策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
下面举一个简单的小例子:
根据情况进行不一样的方案,比如你想去旅游,明确自己有多少钱然后选择旅游方式。
没钱,走路
有钱,飞机
还行,火车
这里就涉及到策略的模式了。
代码如下:
var strategies = {
"rich": function () {
console.log("You can go with plane!");
},
"poor": function () {
console.log("OH, You can go with your feet!");
},
"middle": function () {
console.log("You can go with train!");
}
}
var howShouldGo = function (money) {
return strategies[money]();
}
console.log(howShouldGo("rich"));
五、工厂模式
故名思意,我们从字面上的意思就可以看到,可以想象一座工厂源源不断产出一样的产品,流水线作业。没错,工厂模式就是这样。
我们首先创建一个工厂,我们只要传递参数进去,里面具体的过程我们不用去关心,最后返回一个对象。
代码如下:
function createPerson(n,a,s){
var person = {}; // 原料
// 加工
person.name = n;
person.age = a;
person.sex = s;
person.sayHello = function(){
console.log(this.name + "---" + this.age)
}
// 出厂
return person;
}
var p1 = createPerson("zhangsan",18,"男");
console.log(p1);
p1.sayHello();
var p2 = createPerson("lisi",19,"女");
console.log(p2);
p2.sayHello();
六、MVC模式:M:model数据 V:view视图 C:ctrl控制器
MVC是一个架构设计模式,它通过分离关注点的方式来支持改进应用组织方式。它促成了业务数据(Models)从用户界面(Views)中分离出来,还有第三个组成部分(Controllers)负责管理传统意义上的业务逻辑和用户输入。
代码如下:
// 创建模型,管理多个数据
class Model{
model1(){
return "hello";
}
model2(){
return "world";
}
model3(){
return "你好";
}
}
// 创建视图,管理多种渲染方式
class View{
view1(data){
console.log(data);
}
view2(data){
document.write(data);
}
view3(data){
alert(data);
}
}
// 创建控制器,设定对应的指令
class Ctrl{
constructor(){
// 初始化模型和视图
this.m = new Model();
this.v = new View();
}
// 在指令中,可以读取对应的数据,放在对应的视图中
ctrl1(){
var data = this.m.model1();
this.v.view1(data);
}
ctrl2(){
var data = this.m.model2();
this.v.view3(data);
}
}
var c = new Ctrl();
c.ctrl1();
c.ctrl2();
七、适配器模式:
适配器模式就相当于一个转换接口,大家想想我们手机充电器通常是二岔口的,但是电源只有三岔口的。这时候就需要一个适配器把三岔口的转换成二岔口的。
它的作用其实就是解决两个软件实体间的接口不兼容问题,使用之后就可以一起工作了。
代码如下:
var googleMap = {
show: function () {
console.log('googleMap show!');
}
}
var baiduMap = {
show: function () {
console.log('baiduMap show!');
}
}
var renderMap = function (map) {
if (map.show instanceof Function) {
map.show()
}
}
renderMap(googleMap);
renderMap(baiduMap);
上面这段程序能够运行是因为百度地图和谷歌地图用的同一种show方法,但是我们在不知道对方使用的函数接口的时候,我们就不能这样用了(可能百度是使用了display方法来显示)。下面的baiduMapAdapter就是我们使用的适配器。
代码如下:
var googleMap = {
show: function () {
console.log('googleMap show!');
}
}
var baiduMap = {
display: function () {
console.log('baiduMap show!');
}
}
var renderMap = function (map) {
if (map.show instanceof Function) {
map.show()
}
}
var baiduMapAdapter = {
show:function(){
return baiduMap.display()
}
}
renderMap(googleMap);
renderMap(baiduMapAdapter);
八、外观模式:
为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用。
具体代码如下:
// 三个处理函数
function start() {
console.log('start');
}
function doing() {
console.log('doing');
}
function end() {
console.log('end');
}
// 外观函数,将一些处理统一起来,方便调用
function execute() {
start();
doing();
end();
}
// 调用init开始执行
function init() {
// 此处直接调用了高层函数,也可以选择越过它直接调用相关的函数
execute();
}
init();
九、中介者模式:
所有的相关 对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。
中介者模式使网状的多对多关系变成了相对简单的一对多关系(复杂的调度处理都交给中介者)。
代码如下:
var A = {
score: 10,
changeTo: function(score) {
this.score = score;
// 自己获取
this.getRank();
},
// 直接获取
getRank: function() {
var scores = [this.score, B.score, C.score].sort(function(a, b) {
return a < b;
});
console.log(scores.indexOf(this.score) + 1);
}
};
var B = {
score: 20,
changeTo: function(score) {
this.score = score;
// 通过中介者获取
rankMediator(B);
}
};
var C = {
score: 30,
changeTo: function(score) {
this.score = score;
rankMediator(C);
}
};
// 中介者,计算排名
function rankMediator(person) {
var scores = [A.score, B.score, C.score].sort(function(a, b) {
return a < b;
});
console.log(scores.indexOf(person.score) + 1);
}
// A通过自身来处理
A.changeTo(100); // 1
// B和C交由中介者处理
B.changeTo(200); // 1
C.changeTo(50); // 3
总结:其实设计模式我们经常能用到,只是我们没有注意而已。上面我们说了一些设计模式,还有很多其它的模式,比如命令模式,享元模式,装饰模式等等,后面会持续更新…。
以上如有错误、描述不准确等问题,欢迎大家指正,谢谢…