[译] 你需要知道的7个JavaScript设计模式

451

首页图片

原文地址

设计模式在JavaScript中用于在JavaScript的Web项目中解决一些常见问题的可重复解决方案。

简介

JavaScript设计模式是非常适合作为一种模式去提供问题的解决方案,但这并不能代表可以替代开发人员。

结合开发人员的经验,设计模式可以帮助优化我们的代码,并且寻求问题的解决方案和提供常用的解决方案,而不是片面的代码和语法。

JavaScript设计模式可以协助开发人员写出有组织、优秀且良好结构的代码。
设计模式在使用中虽然很容易出现重复的情况,但却从来无法取代开发人员,相反,设计模式只能通过提供与指定问题关系不大的解决方案去防止开发中的大问题背景下的小问题,从而为开发人员提供帮助。

设计模式通过消除不必要的重复代码来减少整体代码体积,从而使得我们书写的代码更加健壮。

在本文中,我将介绍7种优秀、更受欢迎的JavaScript设计模式,它们整体属于三大类,创建型模式、结构型模式和行为型模式;

1. 构造函数模式

这算得上是一种特殊的模式,它主要用于分配内存后初始化新创建的对象。
由于JavScript是一种面向对象语言,其每时每刻不在处理对象,因此我首先研究这个模式。

一下是创建构造函数模式的一种写法。

var newObject = {}
var newObject = Object.create(Object.prototype)
var newObject = newObject()

如果需要访问对象的属性,首先需要初始化该对象

const object = new ConstructorObject();

关键字new告诉Javascript,ConstructorObject应当是一个构造函数。
在这个模式中是不支持继承的。

2. 原型模式

原型模式是基于原型继承的,其创建的对象会充当其他对象的原型。其实,在创造对象时原型充当的是一个蓝图的角色。
示例

var myCar = {
	name: 'Ford Escort',
	brake: function() {
		console.log('stop! I am applying branks');
	},
	Panic: function() {
		console.log ( “wait. how do you stop thuis thing?”);
	}
}
// 创建一个car实例
var youCar = Object.create(myCar);
console.log(youCar.name); // Ford Escort

3. 模块设计模式

在模块设计模式中,是在原型模式上的一个改进。在模块模式中设置了不同类型的修饰符(私有/共有)。可以创建相似的函数或属性而不会引起冲突。这在命名上具有灵活性,不好的是无法覆盖外部创建的函数。

示例

function AnimalContainer() {
	const container = [];
	function addAnimal(name) {
		container.push(name);
	}
	function getAllAnimal() {
		return container;
	}
	function removeAnimal(name) {
		const index = container.indexOf(name);
		if (index < 1) {
			throw new Error('Animal not found in container');
		}
		container.splice(index, 1);
	}
	return {
		add: addAnimal,
		get: getAllAnimal,
		remove: removeAnimal
	}
}
const container = AnimalContainer();
// 添加
container.add('Hen');
container.add('Goat');
container.add('Sheep');
// 获取
console.log(container.get()); // ['Hen', 'Goat', 'Sheep']
// 移除
container.remove('Sheep');
console.log(container.get()); // ['Hen', 'Goat']

4. 单列模式

当只需要创建一个实例时,比如数据库的连接,这一模式还是很友好的。仅仅创建一个实例,当你确定关闭连接或者打开实例时,确保已经关闭了之前的实例。这种模式也经常被称为严格模式,伴随着这种模式的一个缺点就是在测试时不友好,因为在测试时很难发现其依赖的隐藏起来的对象。

示例

function DatabaseConnection() {
	let databaseInstance = null;
	// 追踪在特定时间下创建的实例数
	let count = 0;
	function init() {
		console.log(`opening database!#${count++}`);
		
	}
	function createInstance() {
		if (databaseInstance === null) {
			databaseInstance = init();
		}
		return databaseInstance;
	}
	function closeInstance() {
		console.log('closing databse');
		databaseInstance = null;
		
	}
	return {
		open: createInstance,
		close: closeIntance
	}
}
const database = DatabaseConnection();
// 仅仅创建一个实例
database.open(); // opening database!#1
database.open(); // opening database!#1
database.open(); // opening database!#1
database.open(); // opening database!#1
database.close(); // close database

5. 工厂模式

这种模式一个创建对象的革新,不再需要构造函数。它提供了一个通用的接口来创建对象,并且我们还可以指定需要创建的工厂对象的类型。因此,我们仅仅指定对象,工厂对象实例化并返回供我们使用。同时当对象组件具有较为复杂时并且我们想要根据不同的环境去创建不同的对象时,使用工厂模式时非常明智的。当使用许多需要共享的属性的轻对象以及组成需要解耦的对象时,我们也可以使用工厂模式。

示例

// Dealer A
DealerA = {};
DealerA.title = function title() {
	return 'Dealer A';
}
DealerA.pay = function pay(amount) {
	console.log('配置用户名: ${this.username} 密码: $	{ this.password }');
	return <code>Payment for service ?{amount} is successful using ${this.title()}</code>;
};
// Dealer B
DealerB = {};
DealerB.title = function title() {
	return 'DealerB';
}
DealerB.pay = function pay(amount) {
	console.log('配置用户名: ${this.username} 密码: $	{ this.password }');
	return <code>Payment for service ?{amount} is successful using ${this.title()}</code>;
}
function DealerFactory(DealerOption, config = {}) {
	const dealer = Object.create(DealerOption);
	Object.assign(dealer, config);
	return dealer;
}
const dealerFactory = DealerFactory(DealerA, {
	username: 'user',
	password: 'password'
});
console.log(dealerFactory.title());
console.log(dealerFactory.pay(12));
const dealerFactory2= DealerFactory(DealerB, {
	username: 'user',
	password: 'password'
});
console.log(dealerFactory2.title());
console.log(dealerFactory2.pay(99));

6. 观察者模式

观察者模式在对象与其他对象集合通信的时候使用还是很方便的。在这个模式中,状态之间没有不必要的pushpull操作,其所设计的模块仅修改数据的当前状态。

示例

function Observer() {
	this.observerContainer = [];
}
Observer.prototype.subscribe = function (element) {
	this.observerContainer.pssh(element);
}
// 从容器中移除一个元素
Observer.prototype.unsubscribe = function (element) {
	const index = this.observerContainer.indexOf(element);
	if (index !== -1) {
		this.observerContainer.splice(index, 1);
	}
}
Observer.prototype.notifyAll = function (element) {
	this.observerContainer.forEach(function (observerElement) {
		observerElement(element);
	});
}

7. 命令模式

在最后,我用命令模式结束了我对JavaScript设计模式的7种最佳总结。命令模式将方法的调用、操作或请求都封装到一个对象之中,便于我们去思考地去调用。命令模式使我们可以向正在执行的命令发布命令,并将责任委托到不同的对象中。这些命令都以run()execute()格式展示。

示例

(function () {
	var carManager = {
		requestInfo: function (model, id) {
			return `the infomation for ${model} with ID ${id} is foo bar`;
		},
		buyVehicle: function (model, id) {
			return `You have successfully purchased Item ${id}, a ${model}`;
		},
		arrangeViewing: function (model, id) {
			return `You have successfully booked a viewing of ${model} (${id})`
		}
	};
})();

作者总结

对于我们JavaScript开发人员来说,这几种设计模式还是很有用的,维护性高的项目和减少开发周期中不必要的工作是设计模式的主要优点。虽然JavaScript设计模式可以为复杂的问题提供解决方案和思路,但是设计模式代替开发人员的结论仍是不恰当的。

个人总结

在日常开发工作中,我常常使用命令模式,而其他模式虽然可以用到,但并不频繁。对于大多数开发者来说,设计模式仅仅是提供了一种特定情景下的解决方案罢了,同时顺便也扩展了自己的思路。同时翻译不妥的地方还请指正,以后的翻译中会更加努力:)