架构-设计模式
简介
设计模式的参考书:Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 总共有 23 中设计模式. 这些模式可以分类三大类: 创建型模式, 结构型模式, 行为型模型.
- 创建型模式: 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式, 而不是使用 new 运算符世界实例化对象. 这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活.
- 结构型模式: 这些设计模式关注类和对象的组合, 继承的概念被哟用来组合接口和定义组合对象获得新功能的方式.
- 行为型模式: 这些设计模式特别关注对象之间的通信.

设计模式的六大原则
开闭原则(Open Close Principle)
对扩展开放, 对修改关闭. 在程序需要进行拓展的时候, 不能去修改原有的代码, 实现一个热插拔的效果. 这个原则是为了使程序的扩展性好, 易于维护和升级. 我们需要使用接口和抽象类, 这在下面具体设计时会提到.里氏代换原则(Liskov Substitution Principle)
任何基类可以出现的地方, 子类一定可以出现. 里氏代换原则是面向对象设计的基本原则之一. LSP 是继承复用的基石, 只有当派生类可以替换掉基类, 且软件单位的功能不受到影响时, 基类才能真正的被复用, 而派生类也能够在基类的基础上增加新的行为. 里氏代换原则是对开闭原则的补充. 实现开闭原则的关键步骤就是抽象化, 而基类与子类的继承关系就是抽象化的具体体现, 所以里氏代换原则是对实现抽象化的具体步骤的规范.依赖倒转原则(Dependence Inversion Principle)
针对接口编程, 依赖抽象而不依赖于具体接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口, 比使用单个接口好 另外的一个意思是: 降低类之间的耦合度.迪米特法则: 最少知道原则(Demeter Principle)
一个实例应当尽量少地与其他实体之间发生相互作用, 使得系统功能模块相对独立合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式, 而不是使用继承
工厂模式
说明
定义一个创建对象的接口, 让其子类自己决定实例化哪一个工厂类, 工厂模式使其创建过程延迟到子类进行.
例子
//工厂模式示意
'use strict';
//创建基类Shape
class Shape {
constructor() {
this.name = 'Shape';
}
say() {
console.log('Father, I am a ', this.name + '.');
}
}
//创建基于基类的子类
class Rectangle extends Shape {
constructor(width, height) {
super();
this.name = 'Rectangle';
this.width = width;
this.height = height;
}
say() {
super.say();
console.log('Child, I am a ', this.name + '.');
}
}
class Square extends Shape {
constructor(length) {
super();
this.name = 'Square';
this.length = length;
}
say() {
super.say();
console.log('Child, I am a ', this.name + '.');
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.name = 'Circle';
this.radius = radius;
}
say() {
super.say();
console.log('Child, I am a ', this.name + '.');
}
}
//创建工厂类
class ShapeFactory {
create(type) {
switch (type) {
case 'Rectangle':
return new Rectangle();
case 'Square':
return new Square();
case 'Circle':
return new Circle();
default:
break;
}
}
}
//主程序
(function main() {
//实例化工厂类
let shapeFactory = new ShapeFactory();
let shape1 = shapeFactory.create('Rectangle');
shape1.say();
let shape2 = shapeFactory.create('Square');
shape2.say();
let shape3 = shapeFactory.create('Circle');
shape3.say();
})();
抽象工厂模式
说明
抽象工厂模式(Abstract Factory Pattern)是指围绕一个超级工厂创建其他工厂. 这个超级工厂又称为其他工厂的工厂.
例子
//抽象工厂模式示意
'use strict';
//创建基类
//形状基类
class Shape {
constructor(area, perimeter) {
this.name = 'shape';
this.area = area;
this.perimeter = perimeter;
}
getArea() {
console.log(this.area);
}
getLength() {
console.log(this.perimeter);
}
}
//颜色基类
class Color {
constructor(colorvalue) {
this.name = 'color';
this.colorvalue = colorvalue;
}
getColor() {
console.log(this.colorvalue);
}
}
//创建子类
class Square extends Shape {
constructor(borderlength) {
super(borderlength * borderlength, borderlength * 4);
this.borderlength = borderlength;
}
getBorderLength() {
console.log(this.borderlength);
}
}
class Red extends Color {
constructor() {
super('red');
}
}
//创建一般工厂类
class ShapeFactory {
create(type, ...value) {
switch (type) {
case 'square':
return new Square(...value);
default:
break;
}
}
}
class ColorFactory {
create(type, ...value) {
switch (type) {
case 'red':
return new Red(...value);
default:
break;
}
}
}
//抽象工厂类
class AbstractFactory {
createFactory(type) {
switch (type) {
case 'shape':
return new ShapeFactory();
case 'color':
return new ColorFactory();
default:
break;
}
}
}
//主程序
(function main() {
let abstractFactory = new AbstractFactory();
let shapeFactory = abstractFactory.createFactory('shape');
let square = shapeFactory.create('square', 15);
square.getArea();
square.getLength();
square.getBorderLength();
})();
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一. 这种类型的设计模式提供了一种创建对象的最佳方式.
这种模式涉及到一个单一的类. 该类负责创建自己的对象, 同时确保只有单个对象被创建. 这个类提供了一种访问其唯一的对象的方式, 可以直接访问, 不需要实例化该类的对象.
注意
- 单例类只能有一个实例.
- 单例类必须自己创建自己唯一的实例.
- 单例类必须给其他所有对象提供这一实例.
说明
- 意图: 保证一个类仅有一个实例, 并提供一个访问的去全局访问点.
- 主要解决:一个全局使用的类频繁地创建与销毁
- 实例:
- 操作一些临界资源时, 只允许一个进程进行访问.(打印机和文件 IO)
- 生产唯一序列号
- 计数器
- 与数据库链接的 I/O
- ...
例子
class Singleton {
constructor(name) {
this.name = name;
this.instance = null;
}
// 构造一个广为人知的接口,供用户对该类进行实例化
static getInstance(name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
}
(function main() {
let singleon = Singleton.getInstance("I'm single");
console.log(singleon.name); //I'm single
let singleton2 = Singleton.getInstance("I'm single2");
console.log(singleton2.name); //I'm single
console.log(singleon === singleton2); //true
})();
建造者模式
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象.
说明
- 意图: 将一个复杂的构建与其表示相分离, 使得同样的构建过程可以创建不同的表示.
- 主要解决: 在软件系统中, 有事面临着"一个复杂对象"的创建工作, 其通常由各个部分的子对象用一定的算法构成, 由于需求的变化, 这个复杂对象的各个部分经常面临着剧烈的变换, 但是将他们组合在一起的算法却相对稳定.
- 关键: 建造者创建和提供实例, 导演管理建造出来的实例的依赖关系.
- 实例:
- 去肯德基,汉堡,可乐,薯条,鸡翅是不变的, 组合䢸经常发生变化生成不同的"套餐"
- 注意: 与工厂模式的区别是, 建造者模式更加关注与零件装备的顺序.
例子
//建造者模式示意
// 创建子类单元
class Kitchen {
constructor() {
this.name = 'kitchen';
}
build() {
console.log('Build the ' + this.name);
}
}
class Bedroom {
constructor() {
this.name = 'bedroom';
}
build() {
console.log('Build the ' + this.name);
}
}
class Livingroom {
constructor() {
this.name = 'livingroom';
}
build() {
console.log('Build the ' + this.name);
}
}
//创建建造者
class Builder {
constructor() {
this.name = 'builder';
}
build() {
let build1 = new Kitchen();
let build2 = new Bedroom();
let build3 = new Livingroom();
build1.build();
build2.build();
build3.build();
}
}
(function main() {
let builder = new Builder();
builder.build();
})();
适配器模式
适配器模式(Adapter Pattern)是作为两个 不兼容的接口之间的桥梁. 这种类型的设计模式属于结构型模式, 它结合了两个独立接口的功能.
这种模式设计到一个单一的类, 该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。我们将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
说明
- 意图:将一个类的接口转换成客户希望的另一个接口。适配器模式器是的原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 主要解决: 主要解决在软件系统中,常常要将一些“现存的对象”放到行的环境中,而新环境的接口是现对象不能满足的。
- 解决方式:继承或依赖
- 关键代码:适配器继承或依赖已有对象,实现想要的目标接口
- 实例:1、美国电器 110v,中国 220v,就要有一个适配器将 110v 转换为 220v。2、Java jdk1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator,两者之间就要使用适配器进行转化。3、在 Linus 系统上运行 Windows 程序。
- 优点:灵活,复用性高,透明度高。
- 缺点:过多的使用适配器,会让系统非常凌乱。
例子
这个实例来自于 汤姆大叔的博客
//适配器模式示意
//首先要先定义鸭子和火鸡的抽象行为,也就是各自的方法函数:
//鸭子
var Duck = function() {};
Duck.prototype.fly = function() {
throw new Error('该方法必须被重写!');
};
Duck.prototype.quack = function() {
throw new Error('该方法必须被重写!');
};
//火鸡
var Turkey = function() {};
Turkey.prototype.fly = function() {
throw new Error(' 该方法必须被重写 !');
};
Turkey.prototype.gobble = function() {
throw new Error(' 该方法必须被重写 !');
};
//然后再定义具体的鸭子和火鸡的构造函数,分别为:
//鸭子
var MallardDuck = function() {
Duck.apply(this);
};
MallardDuck.prototype = new Duck(); //原型是Duck
MallardDuck.prototype.fly = function() {
console.log('可以飞翔很长的距离!');
};
MallardDuck.prototype.quack = function() {
console.log('嘎嘎!嘎嘎!');
};
//火鸡
var WildTurkey = function() {
Turkey.apply(this);
};
WildTurkey.prototype = new Turkey(); //原型是Turkey
WildTurkey.prototype.fly = function() {
console.log('飞翔的距离貌似有点短!');
};
WildTurkey.prototype.gobble = function() {
console.log('咯咯!咯咯!');
};
//为了让火鸡也支持quack方法,我们创建了一个新的火鸡适配器TurkeyAdapter:
var TurkeyAdapter = function(oTurkey) {
Duck.apply(this);
this.oTurkey = oTurkey;
};
TurkeyAdapter.prototype = new Duck();
TurkeyAdapter.prototype.quack = function() {
this.oTurkey.gobble();
};
TurkeyAdapter.prototype.fly = function() {
var nFly = 0;
var nLenFly = 5;
for (; nFly < nLenFly; ) {
this.oTurkey.fly();
nFly = nFly + 1;
}
};
(function main() {
var oMallardDuck = new MallardDuck();
var oWildTurkey = new WildTurkey();
var oTurkeyAdapter = new TurkeyAdapter(oWildTurkey);
//原有的鸭子行为
oMallardDuck.fly();
oMallardDuck.quack();
//原有的火鸡行为
oWildTurkey.fly();
oWildTurkey.gobble();
//适配器火鸡的行为(火鸡调用鸭子的方法名称)
oTurkeyAdapter.fly();
oTurkeyAdapter.quack();
})();
桥接模式
桥接(Bridge)是用于把抽象化与现实化解耦,使得二者可以独立变化。这种设计模式通过提供抽象化和实现化之间的桥接接口,实现二者的解耦。
说明
- 意图:桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- 关键代码:抽象类依赖实现类
例子
//桥接模式示意
//事件监控中的示例
//抽象的功能
function getBeerById(id, callback) {
// 通过ID发送请求,然后返回数据
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// callback response
callback(resp.responseText);
});
}
//实现
addEvent(element, 'click', getBeerByIdBridge);
//桥
function getBeerByIdBridge(e) {
getBeerById(this.id, function(beer) {
console.log('Requested Beer: ' + beer);
});
}
过滤器模式
过滤器模式(Filter Pattern)或标准模式(Crteria Pattern)允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把他们连接起来。这种类型的设计模式结合多个标准来获得单一标准。
例子
class Person {
constructor(name, gender, maritalStatus) {
this.name = name; //姓名
this.gender = gender; //性别
this.maritalStatus = maritalStatus; //婚姻状况
}
getName() {
return this.name;
}
getGender() {
return this.gender;
}
getMaritalStatus() {
return this.maritalStatus;
}
}
function PersonFilter_Name(PersonArray, condition) {
return PersonArray.filter(item => {
return item.getName() == condition;
});
}
(function main() {
let PersonArray = [];
PersonArray.push(new Person('John', 'man', 'single'));
PersonArray.push(new Person('Oakley', 'man', 'married'));
PersonArray.push(new Person('Katharine', 'woman', 'single'));
PersonArray.push(new Person('Callie', 'woman', 'married'));
let filterres = PersonFilter_Name(PersonArray, 'John');
console.log(filterres);
})();
组合模式
组合模式(Composition Pattern),又叫部分整体模式,是用于把一组相似的对象当做一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。 这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
说明
- 意图:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 实例:1. 算式表达式包含操作数,操作符和另一个操作数,其中,另一个操作符可以是操作数,操作符和另一个操作数。
例子
//组合模式示意
class Employee {
constructor(name, dept, sal) {
this.name = name;
this.dept = dept;
this.sal = sal;
this.EmployeeList = [];
}
add(employee) {
this.EmployeeList.push(employee);
}
remove(employee) {
let index = this.EmployeeList.findIndex(item => {
item.name === employee.name;
});
this.EmployeeList.splice(index, 1);
}
printAll() {
console.log(`Employee:[name:"${this.name}",dept:"${this.dept}",sal:"${this.sal}"]`);
this.EmployeeList.forEach(function(item) {
item.printAll();
});
}
}
(function main() {
let CEO = new Employee('John', 'CEO', 30000);
let headSales = new Employee('Robert', 'Head Sales', 20000);
let clerk1 = new Employee('Bob', 'clerk', 10000);
let clerk2 = new Employee('Jack', 'clerk', 10000);
CEO.add(headSales);
headSales.add(clerk1);
headSales.add(clerk2);
//打印全公司
CEO.printAll();
})();
装饰器模式
装饰器模式(Decorator Pattern)允许想一个现有的对象添加新的功能,通常有不改变其结构。这种类型的设计模式数据结构型模式,它是作为现有的类的一个包装。
说明
- 意图:动态的给一个对象添加一个额外的职责。 就增加功能来说,装饰器模式比生成子类更加灵活。
例子
//装饰器模式
//实体类
class Plane {
constructor() {}
fire() {
console.log('发射子弹');
}
}
//装饰类
class PlaneDecorator {
constructor(plane) {
this.plane = plane;
}
fire() {
this.plane.fire();
console.log('发射导弹');
}
}
(function main() {
let plane = new Plane();
let plane2 = new PlaneDecorator(plane);
plane2.fire();
})();
外观模式
外观模式(Facade Pattern)隐藏系统的复杂性, 并向客户端提供了一个客户端可以访问系统的接口. 它向现有的系统添加一个接口, 来隐藏系统的复杂性.
说明
- 意图: 为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口, 这个接口使得这一子系统更加容易使用.
- 何时使用: 1. 客户端不需要知道系统内部的复杂联系, 整个系统只需要提供一个"接待员"即可. 2. 定义系统的入口.
- 实例: 1、去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。 2、JAVA 的三层开发模式。
例子
function startCPU() {
console.log('CPU started');
}
function startMainboard() {
console.log('MainBoard started');
}
function startPower() {
console.log('Power started');
}
//外部接口
function startComputer() {
startCPU();
startMainboard();
startPower();
}
(function main() {
//调用外部接口
startComputer();
})();
享元模式
享元模式(Flyweight Pattern)主要用于减少创建对象的数量, 以减少内存占用和提高性能. 享元模式尝试重用现有同类对象, 如果未找到匹配的对象, 则创建新对象.
说明
- 意图: 运用共享技术有效的支持大量细粒度的对象.
- 主要解决: 存在有大量对象时, 有可能会造成内存溢出, 我们把其中共同的部分抽象出来, 如果有相同的业务请求, 直接返回在内存中已有的对象, 避免重新创建.
例子
//享元模式 示意
/*享元模式优化代码*/
var Book = function(title, author, genre, pageCount, publisherID, ISBN) {
this.title = title;
this.author = author;
this.genre = genre;
this.pageCount = pageCount;
this.publisherID = publisherID;
this.ISBN = ISBN;
};
/* Book工厂 单例 */
var BookFactory = function() {
var existingBooks = {};
return {
createBook: function(title, author, genre, pageCount, publisherID, ISBN) {
/*查找之前是否创建*/
var existingBook = existingBooks[ISBN];
if (existingBook) {
return existingBook;
} else {
/* 如果没有,就创建一个,然后保存*/
var book = new Book(title, author, genre, pageCount, publisherID, ISBN);
existingBooks[ISBN] = book;
return book;
}
}
};
};
代理模式
代理模式(Proxy Pattern)中, 一个类代表另一个类的功能. 在这种模式中, 我们创建具有现有对象的对象, 以便向外界提供功能接口.
说明
- 意图: 为其他对象提供一种代理以控制对这个对象的访问.
- 主要解决: 在直接访问对象是带来的问题, 不如说:要访问的对象在远程的机器上. 在面向对象系统中, 有些对象由于某种原因(比如对象创建的开销比较大, 或者某些操作需要安全控制, 或者需要进程外的访问), 直接访问会给使用者或者系统带来很多麻烦, 我们可以再访问此时对象时加上一个对此对象的访问层.
- 何时使用: 想要在访问一个类是做一些控制.
- 实例: 1. windows 中的快捷方式. 2. 买火车票不一定在火车站买,也可以去代售点.
例子
//代理模式 示意
//实体类
class Image {
constructor(filename) {
this.filename = filename;
}
display() {
console.log('show this image for real');
}
showName() {
console.log(this.filename);
}
}
//代理类
class ProxyImage {
constructor(filename) {
this.Image = new Image(filename);
}
display() {
this.Image.display();
}
showName() {
this.Image.showName();
}
}
(function() {
let proxyImage = new ProxyImage('disk://image.img');
proxyImage.display();
proxyImage.showName();
})();
责任链模式
责任链模式(Chain of Prsponsibility Paterrn)为请求创建了一个接受者对象的链. 这种模式给与请求的类型, 对请求的发送者和接受者进行解耦. 这种类型的设计模式属于行为型模式.
说明
- 意图: 避免请求发送者与接受者耦合在一起, 让多个对象都有可能接受请求, 将这些对象连接成一条链, 并且沿着这条链, 并且沿着这条链传递请求, 知道有对象处理它为止.
- 实例: 1. JS 中的事件冒泡. 2. 击鼓传花
- 使用场景: 1. 有多个对象可以处理同一个请求, 具体哪个对象处理该请求由运行时刻自动确定. 2. 在不明确指定指定接受者的情况下, 向多个对象中的一个提交一个请求. 3. 可动态指定一组对象处理请求.
例子
/*职责链 */
var order500 = function(orderType, isPaid, stock) {
if (orderType === 1 && isPaid === true) {
console.log('500元定金预购,得到100优惠券');
} else {
return 'nextSuccessor';
}
};
var order200 = function(orderType, isPaid, stock) {
if (orderType === 2 && isPaid === true) {
console.log('200元定金预购,得到50优惠券');
} else {
return 'nextSuccessor';
}
};
var orderNormal = function(orderType, isPaid, stock) {
if (stock > 0) {
console.log('普通购买,无优惠券');
} else {
console.log('库存不足');
}
};
Function.prototype.after = function(fn) {
var self = this;
return function() {
var ret = self.apply(this, arguments);
if (ret === 'nextSuccessor') {
return fn.apply(this, arguments);
}
return ret;
};
};
var order = order500.after(order200).after(orderNormal);
order(1, true, 10);
命令模式
命令模式(Command pattern) 是一种数据驱动的设计模式. 请求以命令的形式包裹在对象中, 并传给调用对象. 调用对象寻找可以处理该命令的合适的对象. 并把该命令传给相应的对象, 该对象执行命令.
说明
- 意图: 将一个请求封装成一个对象, 从而使得可以用不同的请求对客户端进行参数化.
- 何时使用: 在某些场合, 比如要对行为进行"记录, 撤销/重做, 事务"等处理, 这种无法抵御变化的紧耦合是不合适的. 在这种情况下, 如何将"行为请求者"与"行为是闲着"解耦? 将一组行为抽象为对象, 可以实现二者之间的松耦合.
例子
// 命令模式示意
//发送者
var setCommond = function(button, fn) {
button.onClick = function() {
fn();
};
};
//执行者
var menu = {
reFresh: function() {
console.log('reFresh');
}
};
//命令对象
var CommondObj = function(reciver) {
return function() {
reciver.reFresh();
};
};
var commonObj = CommondObj(menu);
setCommond(btn, commonObj);
解释器模式
解释器模式(Interpreter Pattern) 提供了评估语言的语法或表达式的方法. 这种模式实现了一个表达式接口, 该接口解释一个特定的上下文. 这种模式被用在 SQL 解析, 符号处理引擎等.
说明
- 意图: 给定一个语言, 定义它的文法接口, 并定义一个解释器, 这个解释器使用该表示来解释语言中的句子.
- 主要解决: 对于一些固定问答构建一个解释句子的解释器.
例子
//解释器模式示意
class TerminalExpression {
constructor(data) {
this.data = data;
}
interpret(context) {
if (context.contains(this.data)) {
return true;
}
return false;
}
}
class OrExpression {
constructor(expr1, expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
interpret(context) {
return this.expr1.interpret(context) || this.expr2.interpret(context);
}
}
class Exporession {
constructor(context) {
this.context = context;
}
contains(data) {
let flag = this.context.indexOf(data);
if (flag) {
return true;
}
return false;
}
}
(function() {
let context = new Exporession('I am groot');
let te = new TerminalExpression('groot');
let res1 = te.interpret(context);
console.log(res1);
let te2 = new TerminalExpression('John');
let orEx = new OrExpression(te, te2);
let res2 = orEx.interpret(context);
console.log(res2);
})();
迭代器模式
迭代器模式是 Java 和.Net 环境中常用的设计模式. 这种模式用于顺序访问集合对象的元素, 不需要知道集合对象的底层表示.
说明
- 意图: 提供一种方法顺序访问一个聚合对象中的各个元素, 而又无序暴露该对象的内部表示;
- 主要解决:不同的方式便利整个整合对象.
- 使用场景: 访问一个聚合对象的内容而无序爆率它的内部表示. 2. 需要为聚合对象提供多种便利方式. 3. 为便利不同的聚合接口提供一个统一的接口
- 实例: JS 中的 iterator
例子
var agg = (function() {
var index = 0,
data = [1, 2, 3, 4, 5],
length = data.length;
return {
next: function() {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element;
},
hasNext: function() {
return index < length;
},
rewind: function() {
index = 0;
},
current: function() {
return data[index];
}
};
})();
中介者模式
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通行复杂度. 这种模式提供了一个中介类, 该类通常处理不同类之间的通行, 并支持松耦合, 使代码易于维护.
说明
- 意图: 用一个中介对象来封装一系列的对象交互, 中介者使和对象不需要显示的相互引用, 从而使其耦合松散, 而且可以独立的改变它们之间的交互.
- 实例: MVC 框架, 其中的 C 就是 M 和 V 的中介者.
- 使用场景: 1. 系统中对象之间存在比较复杂的引用关系, 到时它们之间的依赖关系接口混乱而且难以复用该对象. 2. 向通过一个中间类来分装多个;;类中的行为, 而又不想生成太多的子类.
例子
//中介者模式示意
class User {
constructor(name) {
this.name = name;
}
say(message) {
ChatRoom.sent(this.name, message);
}
}
class ChatRoom {
constructor() {}
static sent(user, message) {
console.log(`${user} says: ${message}. (${new Date()})`);
}
}
(function() {
let user1 = new User('John');
let user2 = new User('Mike');
user1.say("hello i'm John");
user2.say("hello i'm Mike");
})();
备忘录模式
备忘录模式(Memento Pattern) 保存一个对象的某个状态, 以便在适当的时候恢复对象.
说明
- 意图: 在不破坏封装性的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态.
- 应用实例: 1. 后悔药 2. 打游戏的存档 3. Windows 的 Ctrl+z 4. 浏览器中的后退
- 使用场景: 1. 需要保存/恢复数据的相关状态场景. 2. 提供一个可回滚的操作.
例子
class Memento {
constructor(state) {
this.state = state;
}
getState() {
return this.state;
}
}
class Originator {
constructor() {
this.state = null;
}
setState(state) {
this.state = state;
}
getState() {
return this.state;
}
saveSatetToMemento() {
return new Memento(this.state);
}
getStateFromMemento(memento) {
this.state = memento.getState();
}
}
class CareTaker {
constructor() {
this.mementoArray = [];
}
add(state) {
this.mementoArray.push(state);
}
get(index) {
return this.mementoArray[index];
}
}
(function main() {
let originator = new Originator();
let caretaker = new CareTaker();
originator.setState('State #1');
originator.setState('State #2');
caretaker.add(originator.saveSatetToMemento());
originator.setState('State #3');
caretaker.add(originator.saveSatetToMemento());
originator.setState('State #4');
console.log('Current State: ' + originator.getState());
originator.getStateFromMemento(caretaker.get(0));
console.log('First saved State: ' + originator.getState());
originator.getStateFromMemento(caretaker.get(1));
console.log('Second saved State: ' + originator.getState());
})();
观察者模式
当对象间存在一对多的关系是,使用观察者模式(Observer Pattern). 比如, 当一个对象被修改时, 则会自动通知它的依赖对象.
说明
- 意图: 定义对象间的一种一对多的依赖关系, 当一个对象的状态发生改变时, 所有依赖于它的对象都得到同时并被自动更新.
- 应用实例: 拍卖的时候, 拍卖师观察最高标价,然后通知给其他竞价者竞价.
例子
class Subject {
constructor() {
this.OberversArray = [];
this.state;
}
getSate() {
return this.state;
}
setState(state) {
this.state = state;
this.notifyAllObservers();
}
attach(oberver) {
this.OberversArray.push(oberver);
}
notifyAllObservers() {
for (let index = 0; index < this.OberversArray.length; index++) {
const element = this.OberversArray[index];
if (element.update) {
element.update();
} else {
console.log('element is not a oberver');
}
}
}
}
class Observer {
constructor() {}
update() {}
}
//实体观察类
class BinaryOberver extends Observer {
constructor(subject) {
super();
this.subject = subject;
if (this.subject.attach) {
this.subject.attach(this);
} else {
console.log('is not subject');
}
}
update() {
console.log('Binary String: ' + this.subject.getSate());
}
}
(function main() {
let subject = new Subject();
let binary = new BinaryOberver(subject);
subject.setState(15);
subject.setState(10);
})();
状态模式
状态模式(State Pattern)中, 类的行为是基于它的状态改变的. 在状态模式中, 我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象.
说明
- 意图: 允许对象在内部状态发生改变时改变它的行为, 对象看起来好像修改了它的类.
- 主要解决: 对象的行为依赖于它的状态(属性), 并且可以根据它的状态改变而改变它的相关行为.
例子
class StartState {
doAction(context) {
console.log('Player is in start state');
context.setState(this);
}
toString() {
return 'Start State';
}
}
class StopSate {
doAction(context) {
console.log('PLayer is in stop state');
context.setState(this);
}
toString() {
return 'Stop State';
}
}
class Context {
constructor() {
this.state = null;
}
setState(state) {
this.state = state;
}
getState() {
return this.state;
}
}
(function main() {
let context = new Context();
let startstate = new StartState();
startstate.doAction(context);
console.log(context.getState().toString());
let stopstate = new StopSate();
stopstate.doAction(context);
console.log(context.getState().toString());
})();
空对象模式
在空对象(Null Object Pattern)中, 一个空对象取代 NULL 对象实例的检查. Null 对象不是检查空值, 而是反应一个不做任何动作的关系. 这样的 Null 对象也可以在数据不可用的时候提供默认的行为.
例子
//空对象模式 示意
class RealCustomer {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
isNil() {
return false;
}
}
class NullCustomer {
getName() {
return 'Not availble in customer DataBase';
}
inNil() {
return true;
}
}
class CustomerFactory {
constructor() {
this.names = ['Rob', 'Joe', 'Julie'];
}
getCustomer(name) {
for (let i = 0; i < this.names.length; i++) {
const el = this.names[i];
if (el == name) {
return new RealCustomer(name);
}
}
return new NullCustomer();
}
}
(function() {
let cusfac = new CustomerFactory();
let customer1 = cusfac.getCustomer('Rob');
let customer2 = cusfac.getCustomer('Joe');
let customer3 = cusfac.getCustomer('Julie');
let customer4 = cusfac.getCustomer('Laura');
console.log(customer1.getName());
console.log(customer2.getName());
console.log(customer3.getName());
console.log(customer4.getName());
})();
策略模式
在策略模式(Strategy Pattern)中, 一个类的行为或者其算法可以在运行时更改. 在策略模式中, 我们创建表示各种策略的对象和一个行为对着侧列对象改变而改变的 context 对象. 策略对象改变 context 对象的执行算法.
说明
- 意图: 定义一系列的算法, 把它们一个个封装起来, 并且使它们可相互替换.
- 主要解决: 在由多种算法相似的情况下, 使用 if..else 所带来的复杂和难以维护.
- 使用场景: 1. 如果在一个系统里面有许多类, 它们之间的区别仅在于它们的行为, 那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为. 2. 一个系统需要动态地在集中算法中选择一种. 3. 如果一个对象有很多行为, 如果不用恰当的模式, 这些行为就只好使用多重的条件选择语句来实现.
例子
class OperationAdd {
doOperation(num1, num2) {
return num1 + num2;
}
}
class OperationSubstract {
doOperation(num1, num2) {
return num1 - num2;
}
}
class OperationMultiply {
doOperation(num1, num2) {
return num1 * num2;
}
}
class Context {
constructor(strategy) {
this.strategy = strategy;
}
executeStrategy(num1, num2) {
return this.strategy.doOperation(num1, num2);
}
}
(function() {
let context = new Context(new OperationAdd());
console.log(context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
console.log(context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
console.log(context.executeStrategy(10, 5));
})();
模板模式
在模板模式(Template Pattern)中, 一个抽象类公开定义了执行它的方法的方式/模板. 它的子类可以按需要重写方法实现, 但调用将以抽象类中定义的方式进行.
说明
- 意图: 定义一个操作中的算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的接口即可重定义该算法的某些特定步骤.
- 使用场景: 1. 有多个子类共有的方法, 且逻辑相同. 2. 重要的, 复杂的方法, 可以考虑作为模板方法.
例子
class Game {
initialize() {}
startPlay() {}
endPlay() {}
//模板
play() {
//初始化游戏
this.initialize();
//开始游戏
this.startPlay();
//结束游戏
this.endPlay();
}
}
class Cricket extends Game {
constructor() {
super();
}
endPlay() {
console.log('Cricket Game Finished!');
}
initialize() {
console.log('Cricket Game Initialized! Start playing.');
}
startPlay() {
console.log('Cricket Game Started. Enjoy the game!');
}
}
class Football extends Game {
constructor() {
super();
}
endPlay() {
console.log('Football Game Finished!');
}
initialize() {
console.log('Football Game Initialized! Start playing.');
}
startPlay() {
console.log('Football Game Started. Enjoy the game!');
}
}
(function() {
let game = new Cricket();
game.play();
game = new Football();
game.play();
})();
访问者模式
在访问者模式(Visitor Pattern)中, 我们使用了一个访问者类, 它改变了元素类的执行算法. 通过这种方式, 元素的执行算法可以随着访问者改变而改变. 根据模式, 元素对象已接收访问者对象, 这样访问者对象就可以处理元素对象上的操作.
说明
- 意图: 主要键数据结构与数据操作分离
- 何时使用: 需要对一个对象结构中的对象进行很多不同的并且不相关的操作, 而需要避免让这些操作"污染"这些对象的类, 使用访问者模式将这些封装到类中.
例子
//接口
class ComputerPart {
accept(computerPartVisitor) {}
}
//实体类
class KeyBoard extends ComputerPart {
constructor() {
super();
this.name = 'Keyboard';
}
accept(computerPartVisitor) {
computerPartVisitor.visit(this.name);
}
}
class Monitor extends ComputerPart {
constructor() {
super();
this.name = 'Monitor';
}
accept(computerPartVisitor) {
computerPartVisitor.visit(this.name);
}
}
class Mouse extends ComputerPart {
constructor() {
super();
this.name = 'Mouse';
}
accept(computerPartVisitor) {
computerPartVisitor.visit(this.name);
}
}
class Computer extends ComputerPart {
constructor() {
super();
this.parts = [new Mouse(), new KeyBoard(), new KeyBoard()];
}
accept(computerPartVisitor) {
for (let i = 0; i < this.parts.length; i++) {
const el = this.parts[i];
el.accept(computerPartVisitor);
}
}
}
//访问者
class ComputerPartVisitor {
visit(name) {
switch (name) {
case 'Keyboard':
console.log('Displaying Keyboard.');
break;
case 'Mouse':
console.log('Displaying Mouse.');
break;
case 'Monitor':
console.log('Displaying Monitor.');
break;
default:
break;
}
}
}
(function() {
let computer = new Computer();
computer.accept(new ComputerPartVisitor());
})();
MVC 模式
MVC 代表 Model-View-Controller(模型-视图-控制器)模式. 这种模式用于应用程序的分层开发.
说明
- Model: 代表一个存取数据的对象, 它也可以带有逻辑, 在数据变化时更新控制器.
- View: 带白哦模型包含的数据的可视化.
- Controller: 控制器作用域模型和视图上, 它控制数据流向模型对象, 并在数据变化时更新视图. 它使视图与模型分离开
例子
class Student {
constructor(name, rollNo) {
this.name = name;
this.rollNo = rollNo;
}
setRollNo(rollNo) {
this.rollNo = rollNo;
}
getRollNo() {
return this.rollNo;
}
setName(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class StudentView {
printStudentDetail(studentName, studentRollNo) {
console.log(`Student:
Name: ${studentName},
Roll No: ${studentRollNo}`);
}
}
class StudentController {
constructor(model, view) {
this.model = model;
this.view = view;
}
setStudentName(name) {
this.model.setName(name);
}
getStudentName() {
return this.model.getName();
}
setStudentRollNo(RollNo) {
this.model.setRollNo(RollNo);
}
getStudentRollNo() {
return this.model.getRollNo();
}
updateView() {
this.view.printStudentDetail(this.model.getName(), this.model.getRollNo());
}
}
(function() {
let model = new Student('王小明', '0001');
let view = new StudentView();
let controller = new StudentController(model, view);
controller.updateView();
controller.setStudentName('李二狗');
controller.updateView();
})();
业务代表模式
业务代表模式(Business Delegate Pattern)用于对表示层和业务层解耦. 它基本上是用来减少通信对表示层代码中的业务层代码的远程查询功能.
说明
在业务层中我们有以下实体:
- 客户端(Client): 表示层可以是界面相关代码
- 业务代表(Business Delegate): 一个为客户端实体提供的入口类, 它提供了对业务服务方法的访问.
- 查询服务(LookUp Service): 查找服务对象负责获取相关的业务实现, 并提供业务对象对业务代表对象的访问.
- 业务服务(Business Service): 业务服务接口, 实现了该业务服务的实体类, 提供了实际的业务实现逻辑.
例子
//业务代表模式 示意
//实体服务类
class EJBService {
doProcessing() {
console.log('Processing task by invoking EJB Service');
}
}
class JMSService {
doProcessing() {
console.log('Processing task by invoking JMS Service');
}
}
//业务查询服务
class BusinessLookUp {
getBusinessService(serviceType) {
if (serviceType.indexOf('EJB') != -1) {
return new EJBService();
} else {
return new JMSService();
}
}
}
//业务代表
class BusinessDelegate {
constructor() {
this.lookupService = new BusinessLookUp();
this.businessService;
this.serviceType;
}
setServiceType(serviceType) {
this.serviceType = serviceType;
}
doTask() {
this.businessService = this.lookupService.getBusinessService(this.serviceType);
this.businessService.doProcessing();
}
}
//客户端
class Client {
constructor(businessService) {
this.businessService = businessService;
}
doTask() {
this.businessService.doTask();
}
}
(function main() {
let businessDelegate = new BusinessDelegate();
businessDelegate.setServiceType('EJB');
let client = new Client(businessDelegate);
client.doTask();
businessDelegate.setServiceType('JMS');
client.doTask();
})();
组合实体模式
组合实体模式(Composite Entity Pattern)用在 EJB 持久化机制中. 一个组合实体是一个 EJB 实体 bean, 代表了对象的图解.
说明
当更新一个组合实体时, 内部以来对象 beans 会自动更新, 因为它们是由 EJB 实体 bean 管理的.一下是组合实体 bean 的参与者:
- 组合实体(Composute Entity) - 它是主要的实体 bean。它可以是粗粒的,或者可以包含一个粗粒度对象,用于持续生命周期。
- 粗粒度对象(Coarse-Grained Object) - 该对象包含依赖对象。它有自己的生命周期,也能管理依赖对象的生命周期。
- 依赖对象(Dependent Object) - 依赖对象是一个持续生命周期依赖于粗粒度对象的对象。
- 策略(Strategies) - 策略表示如何实现组合实体。
例子
//组合实体模式 示意
//依赖对象
class DependentObject1 {
constructor() {
this.data;
}
setData(data) {
this.data = data;
}
getData() {
return this.data;
}
}
class DependentObject2 {
constructor() {
this.data;
}
setData(data) {
this.data = data;
}
getData() {
return this.data;
}
}
//粗粒度对象
class CoarseGrainedObject {
constructor() {
this.do1 = new DependentObject1();
this.do2 = new DependentObject2();
}
setData(data1, data2) {
this.do1.setData(data1);
this.do2.setData(data2);
}
getData() {
return [this.do1.getData(), this.do2.getData()];
}
}
//组合实体
class CompositeEntity {
constructor() {
this.cgo = new CoarseGrainedObject();
}
setData(data1, date2) {
this.cgo.setData(data1, date2);
}
getData() {
return this.cgo.getData();
}
}
//客户端
class Client {
constructor() {
this.compositeEntity = new CompositeEntity();
}
setData(data1, date2) {
this.compositeEntity.setData(data1, date2);
}
printData() {
for (let i = 0; i < this.compositeEntity.getData().length; i++) {
const el = this.compositeEntity.getData()[i];
console.log('Data: ' + el);
}
}
}
(function() {
let client = new Client();
client.setData('Test', 'Data');
client.printData();
client.setData('Second Test', 'Data1');
client.printData();
})();
数据访问对象模式
数据访问对象模式(Data Access Object Pattern)或 DAO 模式用于把低级的数据访问 API 或操作从高级的业务中分离出来
说明
参与者:
- 数据访问对象接口(Data Access Object Interface) - 该接口定义了在一个模型对象上要执行的标准操作。
- 数据访问对象实体类(Data Access Object concrete class) - 该类实现了上述的接口。该类负责从数据源获取数据,数据源可以是数据库,也可以是 xml,或者是其他的存储机制。
- 模型对象/数值对象(Model Object/Value Object) - 该对象是简单的 POJO,包含了 get/set 方法来存储通过使用 DAO 类检索到的数据。
例子
//数据访问对象模式 示意
//创建数值对象
class Student {
constructor(name, rollNo) {
this.name = name;
this.rollNo = rollNo;
}
setRollNo(rollNo) {
this.rollNo = rollNo;
}
getRollNo() {
return this.rollNo;
}
setName(name) {
this.name = name;
}
getName() {
return this.name;
}
}
//数据访问对象
class StudentDao {
constructor() {
this.students = [new Student('Robert', 0), new Student('John', 1)];
}
deleteStudent(student) {
for (let i = 0; i < this.students.length; i++) {
const el = this.students[i];
if (student.getRollNo() == el.getRollNo()) {
this.students.splice(i, 1);
}
}
console.log('Student: Roll No ' + student.getRollNo() + ', deleted from database');
}
getAllStudents() {
return this.students;
}
getStudent(rollNo) {
for (let i = 0; i < this.students.length; i++) {
const el = this.students[i];
if (el.getRollNo() == rollNo) {
return el;
}
}
}
}
(function() {
let studentDao = new StudentDao();
for (let o = 0; o < studentDao.getAllStudents().length; o++) {
const el = studentDao.getAllStudents()[o];
console.log('Student: [RollNo : ' + el.getRollNo() + ', Name : ' + el.getName() + ' ]');
}
})();
前端控制器模式
前端控制器(Front Controller Pattern)是用来提供一个几种的请求处理机制, 所有的请求都将由一个单一的处理程序处理. 该处理程序可以做认证/授权/记录日志, 或者跟踪请求, 然后把请求传给相应的处理程序.
说明
以下是这种设计模式的实体。
- 前端控制器(Front Controller) - 处理应用程序所有类型请求的单个处理程序,应用程序可以是基于 web 的应用程序,也可以是基于桌面的应用程序。
- 调度器(Dispatcher) - 前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。
- 视图(View) - 视图是为请求而创建的对象。
例子
//前端控制器模式 示意
//创建视图
class HomeView {
show() {
console.log('Displaying Home Page');
}
}
class StudentView {
show() {
console.log('Displaying Student Page');
}
}
//调度器
class Dispatcher {
constructor() {
this.studentView = new StudentView();
this.homeView = new HomeView();
}
dispatch(request) {
if (request.indexOf('STUDENT')) {
this.studentView.show();
} else {
this.homeView.show();
}
}
}
//前端控制器
class FrontController {
constructor() {
this.dispatch = new Dispatcher();
}
isAuthenticUser() {
console.log('User is authenticated successfully.');
return true;
}
trackRequest(request) {
console.log('Page requested: ' + request);
}
dispatchRequest(request) {
//记录每一个请求
this.trackRequest(request);
//对用户进行身份验证
if (this.isAuthenticUser()) {
this.dispatch.dispatch(request);
}
}
}
(function() {
let frontController = new FrontController();
frontController.dispatchRequest('HOME');
frontController.dispatchRequest('STUDENT');
})();
拦截过滤器模式
拦截过滤器模式(Intercepting Filter Pattern)用于对应用程序的请求或响应做一些预处理/后处理. 定义过滤器, 并把在请求传给实际目标应用程序之前应用在请求上. 过滤器可以做认证/授权/记录日志, 或者跟踪请求, 然后吧请求传给相应的处理程序
说明
设计模式的实体:
- 过滤器(Filter) - 过滤器在请求处理程序执行请求之前或之后,执行某些任务。
- 过滤器链(Filter Chain) - 过滤器链带有多个过滤器,并在 Target 上按照定义的顺序执行这些过滤器。
- Target - Target 对象是请求处理程序。
- 过滤管理器(Filter Manager) - 过滤管理器管理过滤器和过滤器链。
- 客户端(Client) - Client 是向 Target 对象发送请求的对象。
例子
//拦截过滤器模式 示意
//实体过滤器
class AuthenticationFilter {
execute(request) {
console.log('Authenticating request: ' + request);
}
}
class DebugFile {
execute(request) {
console.log('request log: ' + request);
}
}
//Target
class Tartget {
execute(request) {
console.log('Executing request: ' + request);
}
}
//过滤器链
class FilterChain {
constructor() {
this.filters = [];
this.target;
}
addFilter(filter) {
this.filters.push(filter);
}
execute(request) {
for (let i = 0; i < this.filters.length; i++) {
const el = this.filters[i];
el.execute(request);
}
this.target.execute(request);
}
setTarget(target) {
this.target = target;
}
}
//过滤器管理器
class FilterManager {
constructor(target) {
this.filterChain = new FilterChain();
this.filterChain.setTarget(target);
}
setFilter(filter) {
this.filterChain.addFilter(filter);
}
filterRequest(request) {
this.filterChain.execute(request);
}
}
//客户端
class Client {
constructor() {
this.filterManager;
}
setFilterManager(filterManager) {
this.filterManager = filterManager;
}
sentRequest(request) {
this.filterManager.filterRequest(request);
}
}
(function main() {
let filterManager = new FilterManager(new Tartget());
filterManager.setFilter(new AuthenticationFilter());
filterManager.setFilter(new DebugFile());
let client = new Client();
client.setFilterManager(filterManager);
client.sentRequest('HOME');
})();
服务定位器模式
服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候。考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在 JNDI 中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。
说明
- 服务(Service) - 实际处理请求的服务。对这种服务的引用可以在 JNDI 服务器中查找到。
- Context / 初始的 Context - JNDI Context 带有对要查找的服务的引用。
- 服务定位器(Service Locator) - 服务定位器是通过 JNDI 查找和缓存服务来获取服务的单点接触。
- 缓存(Cache) - 缓存存储服务的引用,以便复用它们。
- 客户端(Client) - Client 是通过 ServiceLocator 调用服务的对象。
例子
//服务定位器模式 示意
//创建实体服务
class Service1 {
execute() {
console.log('Executing Service1');
}
getName() {
return 'Service1';
}
}
class Service2 {
execute() {
console.log('Executing Service2');
}
getName() {
return 'Service2';
}
}
//为 JNDI 查询创建 InitialContext。
class InitialContext {
lookup(jndiName) {
if (jndiName.indexOf('Service1') != -1) {
console.log('Looking up and creating a new Service1 object');
return new Service1();
} else if (jndiName.indexOf('Service2') != -1) {
console.log('Looking up and creating a new Service2 object');
return new Service2();
}
return null;
}
}
//创建缓存Cache
class Cache {
constructor() {
this.services = [];
}
getService(servicesName) {
for (let i = 0; i < this.services.length; i++) {
const el = this.services[i];
if (el.getName().indexOf(servicesName) != -1) {
console.log('Returning cached ' + servicesName + ' object');
return el;
}
}
return null;
}
addService(newServices) {
let exists = false;
for (let i = 0; i < this.services.length; i++) {
const el = this.services[i];
if (el.getName().indexOf(newServices.getName())) {
exists = true;
}
}
if (!exists) {
this.services.push(newServices);
}
}
}
//创建服务定位器
class ServiceLocator {
constructor() {
this.cache = new Cache();
}
getService(jndiName) {
let service = this.cache.getService(jndiName);
if (service != null) {
return service;
}
let context = new InitialContext();
let service1 = context.lookup(jndiName);
this.cache.addService(service1);
return service1;
}
}
(function() {
let serviceLocator = new ServiceLocator();
let service = serviceLocator.getService('Service1');
service.execute();
service = serviceLocator.getService('Service2');
service.execute();
service = serviceLocator.getService('Service1');
service.execute();
service = serviceLocator.getService('Service2');
service.execute();
})();
传输对象模式
传输对象模式(Reansfer Object Pattern)用于从客户端向服务器一次性传递带有多个属性的数据. 传输对象也被称为数值对象. 传输对象是一个具有 getter/setter 方法的简单的 POJO 类,它是可序列化的,所以它可以通过网络传输。它没有任何的行为。服务器端的业务类通常从数据库读取数据,然后填充 POJO,并把它发送到客户端或按值传递它。对于客户端,传输对象是只读的。
说明
客户端可以创建自己的传输对象,并把它传递给服务器,以便一次性更新数据库中的数值。以下是这种设计模式的实体。
- 业务对象(Business Object) - 为传输对象填充数据的业务服务。
- 传输对象(Transfer Object) - 简单的 POJO,只有设置/获取属性的方法。
- 客户端(Client) - 客户端可以发送请求或者发送传输对象到业务对象。
例子
//传输对象模式 示意
//创建传输对象
class StudentVO {
constructor(name, rollNo) {
this.name = name;
this.rollNo = rollNo;
}
setRollNo(rollNo) {
this.rollNo = rollNo;
}
getRollNo() {
return this.rollNo;
}
setName(name) {
this.name = name;
}
getName() {
return this.name;
}
}
//创建业务对象
class StudentBO {
constructor() {
this.students = [];
let student1 = new StudentVO('Robert', 0);
let student2 = new StudentVO('John', 1);
this.students.push(student1);
this.students.push(student2);
}
deleteStudent(student) {
for (let i = 0; i < this.students.length; i++) {
const el = this.students[i];
if (el.getName() == student.getName()) {
this.students.splice(i, 1);
}
}
console.log('Student: Roll No ' + student.getRollNo() + ', deleted from database');
}
getAllStudents() {
return this.students;
}
getStudent(rollNo) {
return this.students[rollNo];
}
updateStudent(student) {
this.students[student.getRollNo()].setName(student.getName());
console.log('Student: Roll No ' + student.getRollNo() + ', updated in the database');
}
}
(function() {
let studentBuinessObj = new StudentBO();
for (let i = 0; i < studentBuinessObj.getAllStudents().length; i++) {
const student = studentBuinessObj.getAllStudents()[i];
console.log('Student: [RollNo : ' + student.getRollNo() + ', Name : ' + student.getName() + ' ]');
}
let student = studentBuinessObj.getAllStudents()[0];
student.setName('Michael');
studentBuinessObj.updateStudent(student);
})();
发布 - 订阅模式
发布订阅模式与观察者模式的区别:

const PubSub = (() => {
const topics = {}; //保存订阅主题
const subscribe = (type, fn) => {
//订阅某类型主题
if (!topics[type]) {
topics[type] = [];
}
topics[type].push(fn);
};
const publish = (type, ...args) => {
//发布某类型主题
if (!topics[type]) {
return;
}
for (let fn of topics[type]) {
//通知相关主题订阅者
fn(args);
}
};
return { subscribe, publish };
})();
let subA = { type: 'event1' },
subB = { type: 'event2' },
subC = { type: 'event1' };
PubSub.subscribe(subA.type, () => console.log(`update eventType: ${subA.type} subA`)); //订阅者A订阅topic1
PubSub.subscribe(subB.type, () => console.log(`update eventType: ${subB.type} subB`)); //订阅者B订阅topic2
PubSub.subscribe(subC.type, () => console.log(`update eventType: ${subC.type} subC`)); //订阅者C订阅topic1
PubSub.publish(subA.type); //发布topic通知,通知订阅者A、C