JavaScript 设计模式是一种可重用的解决方案,旨在解决软件设计中的常见问题。这些解决方案支持创建优化方法,以基于更少的设计模式来解决大量问题类型。设计模式的好处包括:
1. 行之有效的解决方案:许多软件开发人员都使用设计模式。被许多开发人员成功使用,我们在实现给定的设计模式时获得了更大的成功确定性。当设计模式成为主流时,您可以放心地知道它们已经被多次修改和重构。经过验证的解决方案通常是最优的,考虑到边缘情况,并且可用于各种用例。
2. 易于重用:当解决方案可重用时,可以对其进行修改以解决多个特定问题。设计模式记录了一个可重用的解决方案,该解决方案与任何特定问题无关,而是与设计模式帮助克服的一系列挑战相关。
3. 富有表现力:很多时候,设计模式可以以简明扼要的方式解释大型解决方案。
4. 降低重构代码的需求:大多数设计模式都会考虑代码依赖性、现有和未来的依赖性。例如,开放封闭设计原则 - 无需重构已编写的代码。相反,您创建一个新类(用其他非 js 语言实现接口)并添加代码。您可以限制使用设计模式重构代码的需要。
5. 简化沟通:由熟悉设计模式的软件工程师组成的团队能够通过其代码库语言更轻松地进行内部沟通。他们还能够与外部沟通潜在的解决方案、值得注意的未来问题以及所有架构设计。设计模式简化了沟通。
6. 精简代码库站点:由于其优雅、高效且经过深思熟虑的方法,设计模式通常需要更少的代码,最终只需一个团队的代码库。
甚至还不太接近,一开始 JavaScript 更像是“粘合剂”,允许您将 HTML 元素的各种显示粘在一起。最初被称为客户端脚本语言,世界上第一个网络浏览器之一 - Netscape Navigator,利用 JavaScript 显示静态 HTML。
当然,这导致了我们现在所说的
浏览器之战
。
浏览器是新的、热门的、爆炸式的——它们是科技行业的下一个重大事件。Mozilla(以前称为 Netscape Communications)、Microsoft Explorer 以及最终的 Chrome 等大玩家都在争夺浏览器的荣耀。
作为这场浏览器战争中每个浏览器背后的驱动力,大公司们正在研究、开发和创建新的和改进的方法来实现自己的客户端脚本语言。
Netscape:
JavaScript(实际上,Brendan Eich 创建了最初的 JS)
微软:
JScript(有人知道那是什么吗?)
作为那个时代的开发人员,我想象到了巨大的挫败感。这些实现有很大不同。开发并不针对所有浏览器,而是针对个别浏览器。
正如我所想象的那样,愤怒的软件工程师拿着干草叉和火把聚集在一起,其数量可与我们世界历史上最恶意的战争的军队相媲美。随着愤怒的开发人员的崛起,我们只有一个需求 - 为所有浏览器选择一种 MO**** FU***** 语言。
(在我的想象中,我们的开发者祖先在这个时代更像是伟大的维京战士,渴望战争,并在争取简化和荣耀的战斗中面临死亡。我最近也在 Hulu 上观看维京人 - 这可能让我的想象力猖獗。 .)
于是,ECMAScript诞生了。
你问的 ECMAScript 是什么?自由、包容性和非疯狂标准化的呼声。
ECMAScript 是所有现代浏览器都试图支持的标准化脚本语言规范。如果您想进行传统的人类语言类比,它确实有许多更像不同方言的实现。
我喜欢将 ECMAScript 视为所有脚本语言和 JavaScript 的始祖,因为它是英雄的儿子 - 英雄中的英雄,战胜一切困难的斗士,因为它太棒了而赢得了所有女士的青睐(但说实话,JavaScript 是各位工程师最常用的软件语言之一)
JavaScript 是源自 ECMAScript 的最流行的方言。
由于 ECMAScript 进入了现实世界,它为软件工程社区做了一些重要的事情。它标准化了维基百科上列出的许多重要内容。
JavaScript 是一种轻量级、解释性、面向对象的编程语言,具有一流的功能,最常被称为网页脚本语言。它具有以下特点:
- 内存占用低
- 容易实现
- 简单易学
- 语法与其他流行语言(例如 C++ 和 Java)类似
- 是脚本语言,这意味着它的代码是解释而不是编译的
- 有程序支持
- 具有面向对象的支持
- 具有函数式编程风格支持
- 对于开发者来说很灵活!
JavaScript 支持一流函数,这些函数非常强大,但一开始也有些难以掌握。据说一种编程语言具有一流函数只是意味着该语言中的函数被像任何其他变量一样对待。第一类函数可以作为参数传递给其他函数。
以下是重构后的代码:
```javascript
// 定义一个函数作为参数传递
function action(name, payload, callback_function) {
let context = { name, payload };
callback_function(context);
};
// 调用 action 函数
action('log-alert', 'hello world', function (context) {
console.log(
'The action context:', context,
'The action name:', context.name,
'The action payload:', context.payload
);
});
// 第一类函数可以由另一个函数返回
function sayHello() {
return function() {
console.log('Hello!');
};
}
// 第一类函数可以作为值分配给变量并调用它
const foo = function() {
console.log('foobar');
};
foo(); // 调用变量中的函数
```
我们将在本文的设计模式部分进一步深入探讨。理解原型对于 JavaScript 来说非常重要,但我们暂时不要添加太多细节。
JavaScript 事件循环
您听说过回调函数吗?如果您习惯在 JavaScript 世界中工作,我相信您已经习惯了。回调函数是作为参数发送到另一个函数的函数(由于函数是一等公民,因此可以接受)。作为参数传递的函数将在事件触发后调用。通常,这用于订阅事件。示例:鼠标右键单击事件触发要调用的函数 - 因此是回调函数。
事件有一个附加的侦听器。每次该事件触发时(否则该事件将丢失),消息被发送到消息队列。该消息队列(FIFO - 先进先出)同步处理。这个过程就是我们所知的 JavaScript 事件循环。
每个队列消息具有关联功能。一旦队列消息出队,运行时在处理任何其他消息之前完全执行该函数。如果一个函数包含其他函数调用,它们都是在处理队列中的新消息之前执行的。这称为运行到完成。
```javascript
while (queue.waitForMessage()) { queue.processNextMessage();}
```
队列.waitForMessage() 同步等待新消息。加工完成后,从队列中处理一条新消息(如果有的话)。每条正在处理的消息都有自己的堆栈,并进行处理,直到堆栈为空。
您是 Clean Code Studio 内容的粉丝吗?“加入时事通讯以获取更多此类内容!”您是否听说过 JavaScript 中使用的“非阻塞”或异步术语?当执行异步操作时,它不会停止或停止运行时,该程序能够处理其他事情,例如接收用户输入。在等待异步操作完成时,异步操作对于主执行线程来说是非阻塞的。这是一项非常有用的功能,既可以在 JavaScript 内部使用,也可以在外部用于特定的 JavaScript 用例。异步与同步是 JavaScript 中的一个巨大主题,但如果我们也深入研究,我们永远不会了解我们的设计模式 - 这是本文讨论的主题。
什么是设计模式?设计模式是软件设计中常见问题的可重用解决方案。我们来看看设计模式的一些类别。
原型模式:创建设计模式,你是如何做到的?您是否注意到任何经常重复出现的问题?您是否已经克服了为解决此问题而专门设计的解决方案?假设您的这个解决方案没有得到全球认可和记录。
每次出现或遇到此问题时,您都可以使用以下解决方案。这个解决方案是可重用的,整个开发人员社区都将受益于这种模式。
这并不会立即使其成为一种设计模式。程序员可能拥有良好的代码,但只是将一些看起来像模式的东西误认为是实际的设计模式——而归根结底——它不是真正的设计模式。
那么,什么使某些东西成为实际的设计模式呢?
答:开发者普遍共识。如果您能够从大量开发人员那里获得意见,那么您就走在正确的道路上。通过了解创建模式本身的过程,并让自己熟悉现有模式,您就开始学习该过程。任何设计模式都必须经历这个阶段才能成为成熟的模式。这称为原型模式。
如果原型模式满足一定时期的测试所定义的标准,并且必须由不同数量的开发人员进行测试,那么它就是未来的模式。它必须在许多挑战的背景下进行测试,在许多场景中进行分析,并最终通过许多测试和普遍的社区共识被证明是一种有用且可重用的设计模式。为了展示如何使给定软件语言的开发人员社区认可成熟的模式,我们已经完成了大量的工作和文档。
同样值得注意的是,与软件中的许多事物一样,给定概念的倒置也是如此。设计模式的反面是什么?
反模式:对象关系相反。例如,工厂设计模式通常用于创建对象,而单例设计模式则用于限制对象的数量。
设计模式可以分为多种类别,以下是一个流行的分类:
- 创意:创建对象的模式。这些是优化创建单个或一组对象的机制的设计模式。
- 建造者设计模式、工厂设计模式、单例设计模式、原型设计模式、抽象工厂设计模式等都是创意设计模式的例子。
- 结构:与对象关系相关。这些类型的设计模式确保如果系统的一部分发生变化,整个系统不需要随之改变。这些包括代理设计模式、桥梁设计模式、立面设计模式、适配器设计模式、装饰设计模式、享元设计模式和复合设计模式等都是结构设计模式的例子。
- 行为:识别、实现和改进系统中对比对象之间的通信。它们用于支持给定软件系统的对比部分具有同步数据。这些包括状态设计模式、访客设计模式、命令设计模式、纪念品设计模式、迭代器设计模式、中介者设计模式和观察者设计模式等都是行为设计模式的例子。
- 并发:用于实现多线程编程范例的解决方案。这些包括调度程序设计模式、主动对象设计模式和核反应等都是并发设计模式的例子。
并发设计模式的例子
架构设计模式用于实现架构最佳实践。以下是一些常见的架构设计模式:
1. MVP 设计模式(模型-视图-演示者):这种模式将应用程序分为三个部分:模型、视图和演示者。模型负责处理数据,视图负责显示数据,演示者负责更新视图以反映模型的更改。
2. MVC设计模式(模型-视图-控制器):这种模式将应用程序分为三个部分:模型、视图和控制器。模型负责处理数据,视图负责显示数据,控制器负责处理用户输入并更新模型和视图。
3. MVVM 设计模式(模型-视图-视图模型):这种模式将应用程序分为三个部分:模型、视图和视图模型。模型负责处理数据,视图负责显示数据,视图模型负责将数据绑定到视图。
这些都是架构设计模式的例子。每一种设计模式都代表特定类型问题的特定类型解决方案。最好的设计模式从来都不是通用的。为了成为最好的软件工程师,我们需要学习何时应该使用给定的设计模式。我们需要从上下文的角度了解哪种设计模式是最好的。
对于给定的问题使用不正确的设计模式不仅没有帮助,而且可能会损害我们和我们的应用程序目标。例如,在 JavaScript 中创建对象时,我们需要了解如何使用不同的方法来添加属性和初始化对象。以下是一些常见的方法:
1. 构造函数模式:在经典的面向对象软件语言中,构造函数是我们首先了解的特殊函数之一。这是我们用来用一组默认属性值初始化对象的函数。
2. 点符号表示法:创建对象后,有四种方法(自 ES3 起)可以实际向新创建的 js 对象添加属性。
3. 括号表示法:创建对象后,可以使用括号表示法向新创建的 js 对象添加属性。
4. Object.defineProperties 表示法:创建对象后,可以使用 Object.defineProperties 方法向新创建的 js 对象添加属性。
5. 大括号表示法:大括号表示法是在 JavaScript 中创建对象的最流行的方法。点符号或方括号是定义属性和设置这些属性值的最流行的方法。
在JavaScript中,模块化设计模式是一种常见的设计模式,它可以使得代码更加模块化、易于维护和复用。在这个问题中,我们可以看到两种不同的实现方式:一种是使用函数式编程的思想,另一种是使用面向对象编程的思想。
第一种实现方式是通过函数来实现模块化,这种方式比较简单易懂,但是不太适合大型项目。第二种实现方式是通过类来实现模块化,这种方式比较适合大型项目,因为它可以让代码更加清晰、易于维护和复用。
下面是一个使用函数式编程思想的例子:
```javascript
function Person(name, email, admin) {
let isAdmin = function() {
return admin === true;
};
let isNotAdmin = function() {
return admin === false;
};
this.name = name;
this.email = email;
this.isAdmin = isAdmin;
this.isNotAdmin = isNotAdmin;
}
let tim = new Person('Tim', 'tim@gmail.com', false);
let sarah = new Person('Sarah', 'sarah@gmail.com', true);
console.log(tim.isAdmin()); // false
console.log(tim.isNotAdmin()); // true
console.log(sarah.isAdmin()); // true
console.log(sarah.isNotAdmin()); // false
```
JavaScript 是一种强大的编程语言,它总是能够实现一些奇特的功能,让人惊叹不已。有时,这些特性可能会让人感到困惑,但它们也为我们提供了实现一些非常强大的模式的能力。
与其他编程语言相比,JavaScript 的一个特点是它支持访问修饰符。在深入研究模块模式之前,让我们先了解一下 JavaScript 中的闭包。理解闭包对于真正理解 JavaScript 中一些最强大的模式至关重要。
闭包是一个可以访问父作用域的函数,即使在父函数关闭之后也是如此。闭包帮助我们通过作用域来模仿访问修饰符的行为。下面是一个例子来说明这一点:
```javascript
let Countable = (function () {
let count = 0;
return function () {
return count++;
};
})();
console.log(Countable()); // 1
console.log(Countable()); // 2
console.log(Countable()); // 3
```
在这个示例中,我们使用了立即调用函数表达式(IIFE),也称为立即执行函数表达式。每次我们调用 `countable` 时,它所绑定的函数都会立即执行。我们能够做到这一点要归功于 JavaScript 中函数作为一等公民的力量。
通过闭包的力量,我们还可以创建具有私有和公共部分的对象,这些对象被称为模块。当我们需要隐藏对象的某些子部分的行为时,这些模块就非常有用。我们可以根据需要修改哪些行为是公开的,哪些部分是私有的而不是公开的。以下是一个示例:
```javascript
const privateVar = "I am private";
const publicVar = "I am public";
function MyClass() {
this.publicVar = publicVar;
}
MyClass.prototype.getPrivateVar = function() {
return privateVar;
};
const myInstance = new MyClass();
console.log(myInstance.publicVar); // "I am public"
console.log(myInstance.getPrivateVar()); // "I am private"
```
```javascriptconst Collection = (function() {
// items is a private property
let items = [];
// everything returned engulfed public properties and methods
return {
add: function(item) {
items.push(item);
},
remove: function(item) {
let index = items.indexOf(item);
if (index >= 0) {
items.splice(index, 1);
}
},
all: function() {
return JSON.parse(JSON.stringify(items));
}
}
})();
Collection.add('Tim');
Collection.add('Sarah');
Collection.add('Raphael');
console.log(Collection.all()); // [ 'Tim', 'Sarah', 'Raphael' ]
Collection.remove('Sarah');
console.log(Collection.all()); // [ 'Tim', 'Raphael' ]
```
让我们改进上面说明的模块设计模式。我们的主要区别在于,我们将在模块的私有范围内编写整个对象逻辑,然后通过返回匿名对象来公开我们想要公开的部分。当将私有成员映射到相应的公共成员时,我们还可以更改私有成员的命名。
```javascript
const Collection = (function () { /* Private Members */ let items = [] function all () { return JSON.parse(JSON.stringify(items)) } function add (item) { items.push(item) } function remove (item) { let index = items.indexOf(item) if (index >= 0) items.splice(index, 1) } /* Public Members */ return { addItem: add, allItems: all, removeItem: remove, }})()
Collection.addItem('Tim')
Collection.addItem('Sam')
Collection.addItem('Ben')
console.log(Collection.allItems()) // ['Tim', 'Sam', 'Ben']
Collection.remove('Sam')
console.log(Collection.allItems()) // ['Tim', 'Ben']
```
上面直接显示的这个示例就是所谓的揭示模块模式。这是我们能够实现模块模式的至少3种不同方式之一。揭示模块模式与模块设计模式的所有其他变体之间有什么区别?差异主要取决于公共成员的引用方式。结果,揭示模块设计模式更容易使用和修改。话虽如此,这种设计模式在某些情况下可能很脆弱(请记住,没有一种设计模式是普遍适用的)。
在考虑使用揭示模块模式时,我们需要关注以下几个问题场景:
1. 私有函数与公共函数的覆盖:当一个私有函数实际上是指向公共函数时,我们将无法使用揭示模块模式来覆盖它。这是因为尝试覆盖它会导致引用私有实现的错误,从而影响软件的正确性。
2. 公共成员与私有变量的覆盖:如果我们有一个指向私有变量的公共成员,并试图从模块外部覆盖它,那么这种做法也不推荐。因为其他函数仍然会使用该私有变量的值,导致软件中的错误。
3. 单例设计模式的应用场景:单例设计模式用于确保我们只需要一个类的实例。例如,在配置设置的情况下,我们可能需要在整个应用程序运行期间只加载一次配置对象。这时就可以使用单例设计模式。用户定义的配置设置只需一次性加载到一个对象中,以便 JS 运行时可以访问它们。这样,在每次尝试访问配置设置时,就无需重新创建该对象。
```javascriptconst Singleton = (function () {
// Private config
let config;
function initializedConfigurationSettings(values) {
this.random = Math.random();
values = values || {};
this.number = values.number || 5;
this.size = values.size || 10;
}
return {
getConfig: function (values) {
// we initialize the singleton value only once
if (config === undefined) {
config = new initializedConfigurationSettings(values);
}
return config;
},
};
})();
const ConfigurationSettings = singleton.getConfig({ app: "HelloWorld", environment: "local" });
console.log(ConfigurationSettings); // { app: "HelloWorld", environment: "local" }
ConfigurationSettings.getConfig({ "number": 8 }); // same randomDecimalValue as in the first config - aka we've proven it's the same object
singleton.getConfig
// Observing pattern
in my opinion, the observer pattern is one of the most powerful design patterns -- especially in JavaScript.
The observer pattern is a behavioral design pattern that we can use to improve communication between parts of our software application. It involves creating a subject, an observer, and defining how the observer reacts to changes made to the subject.
在实现这种设计模式时,确实存在多种变体,但其最基本的形式包括两个主要部分。
第一部分:主题
主题负责处理与某个主题相关的所有操作。观察家订阅了这个主题,从而可以获取到主题的最新状态和变化。
第二部分:观察者
观察者可以订阅和取消订阅主题。这使得观察者能够根据需要灵活地关注感兴趣的主题。
为了帮助您更好地理解这种设计模式,我们可以通过一个例子来说明:假设我们有两种类型的对象,即客户和商店。客户对特定品牌的产品(如iPhone)感兴趣,而该产品很快就会在商店中上架。让客户每天访问商店并检查产品可用性是资源密集型的。相反,客户可以订阅商店提供的iPhone主题。这样,客户就可以获取到关于iPhone上架的实时信息,而无需亲自去商店查看。
解决方案:
具有某种有趣状态的客体就是主体。由于它还将通知其他对象有关其状态的更改,我们也将其称为发布者。所有其他对象(在我们的例子中为客户)都将成为订阅者。通过这种方式,设计模式允许我们在不牺牲性能的情况下实现松散耦合和动态更新。
```javascriptlet publisher = {}(function (container) {
// represents a unique subscription id to a topic
let id = 0;
container.subscribe = function (topic, callback) {
if (!(topic in container)) container[topic] = [];
container[topic].push({ id: id++, callback: callback });
return id;
}
container.unsubscribe = function (topic, id) {
let subscribers = [];
for (let subscriber of container[topic]) {
if (subscriber.id !== id) subscribers.push(subscriber);
}
container[topic] = subscribers;
}
container.publish = function (topic, data) {
for (let subscriber of container[topic]) {
subscriber.callback(data);
}
}
})(publisher);
let subscription_1 = publisher.subscribe('mouseClicked', function (data) {
console.log(
'Sam\'s callback for mouse click: ',
'Event Data: ', JSON.stringify(data)
);
});
let subscription_2 = publisher.subscribe('mouseHovered', function (data) {
console.log(
'Sam\'s callback for mouse hovered: ',
'Event Data: ', JSON.stringify(data)
);
});
let subscription_3 = publisher.subscribe('mouseClicked', function (data) {
console.log(
'Sarah\'s callback function for mouse click: ',
'Event Data: ', JSON.stringify(data)
);
});
publisher.publish('mouseClicked', { data: 'data1' });
publisher.publish('mouseHovered', { data: 'data2' });
// unsubsribe from an event publisher.unsubcribe('mouseClicked', subscription_3);
publisher.publish('mouseClicked', { data: 'data1' });
publisher.publish('mouseHovered', { data: 'data2' });
```
观察者设计模式在需要针对单个事件执行多个操作时非常有用。举个例子,假设我们需要对API进行多次AJAX调用,并根据初始调用集返回的数据结果执行其他AJAX调用。如果将这些AJAX调用嵌套在另一个调用中,可能会导致回调地狱的情况。这时使用发布者/订阅者模式是一个更优雅的解决方案。
然而,观察者设计模式也有一些缺点。首先,测试系统的各个部分可能会变得困难。其次,由于JavaScript不支持经典OOP本机理解中的类,对象之间的继承是通过基于原型的编程来实现的。这使得我们能够创建原型对象,这些对象可以充当JavaScript中创建的其他对象的原型。原型对象用作构造函数创建的每个对象的蓝图。
下面是一个简单的原型模式实现示例:
```javascript
// 定义一个原型对象
function MyObjectPrototype() {
this.property1 = "default value";
}
// 为原型对象添加方法
MyObjectPrototype.prototype.myMethod = function() {
console.log("This is a method from the prototype");
};
// 根据原型对象创建一个构造函数
function MyObject(props) {
// 将传入的属性与原型对象的属性合并
for (var property in MyObjectPrototype) {
if (MyObjectPrototype.hasOwnProperty(property)) {
this[property] = props[property] || MyObjectPrototype[property];
}
}
}
// 创建一个新的对象实例
var myInstance = new MyObject({ property2: "custom value" });
// 调用从原型中继承的方法
myInstance.myMethod(); // 输出 "This is a method from the prototype"
console.log(myInstance.property1); // 输出 "default value",因为 property1 是原型对象的属性,而不是 myInstance 的属性
```
原型在 JavaScript 中是实现继承的一种方式,下面是一个简单的示例。
首先,我们定义了一个名为 `PersonPrototype` 的对象,它包含两个方法:`hi` 和 `bye`。这两个方法都使用了 `this.name` 和 `this.age` 属性来输出个人信息。
接下来,我们定义了一个名为 `Person` 的构造函数,它接受两个参数:`name` 和 `age`。如果没有提供这些参数,它们将分别默认为 "John Doe" 和 26。在 `Person` 函数内部,我们定义了一个名为 `constructorFunction` 的函数,它接受相同的参数,并使用这些参数设置 `this.age` 和 `this.name` 属性。然后,我们将 `constructorFunction.prototype` 设置为 `PersonPrototype`,以便将原型链指向正确的对象。最后,我们使用 `new` 关键字创建一个新的对象,并将其返回。
现在,我们可以使用 `Person` 构造函数创建两个新的 `Person` 对象:`person1` 和 `person2`。调用它们的 `hi` 方法,可以看到它们的信息不同。
```javascript
let PersonPrototype = { hi: function () { console.log(`Hello, my name is ${this.name}, and I'm ${this.age}.`) }, bye: function () { console.log(`I'm ${this.name} and I'm saying bye now!`) }}function Person (name, age) { age = age || 26 name = name || "John Doe" function constructorFunction(name, age) { this.age = age this.name = name } constructorFunction.prototype = PersonPrototype let obj = new constructorFunction(name, age) return obj}let person1 = Person()let person2 = Person("Tim", 38)person1.hi() // "hello, my name is John Doe and I'm 26person2.hi() // "hello, my name is Tim and I'm 38
```
在这个示例中,我们实现了一个简单的原型继承。但是,命令设计模式是一种更高级的设计模式,它可以将执行命令的对象与发出命令的对象分离。这在处理 API 调用等场景时非常有用。
调用API服务的对象分开。实现这一设计模式可以避免在需要调用服务的所有位置修改代码。相反,我们必须在本身进行调用的对象上进行更改,这意味着我们只需在一处而不是多个地方进行更改。
谈到设计模式时,一个重要的要点是,我们需要习惯于理解在决定使用任何给定设计模式时所做的权衡。我们是否添加了不需要的抽象层,或者我们是否正在解决需要抽象层正确解决的依赖侵蚀问题?
以下是一个JavaScript代码示例:
```javascript
let invoker = { add: function (x, y) { return x + y }, subtract: (x, y) { return x - y },}let manager = { execute: function (name, args) { if (name in invoker) { return invoker[name].apply(invoker, [].slice.call(arguments, 1)) } return false }}console.log(manager.execute("add", 3, 5)) // 8console.log(manager.execute("subtract", 5, 3)) // 2
```
立面设计模式
什么是门面设计模式?使用这种设计模式,我们能够在公开显示的内容和幕后实现的内容之间创建一个抽象层。这种设计模式对于提高可读性非常有用。
这种模式的一个很好的例子是来自 DOM 操作库(例如 jQuery、Dojo 或 D3)的选择器。您可能已经注意到使用这些库时它们具有非常强大的选择器功能;您可以编写复杂的查询,例如:
jQuery(".parent .child div.span")
在这个例子中,选择特征语法被简化了很多。尽管表面上看起来很简单,但所实现的场景背后的逻辑要复杂得多。在幕后,我们做了很多工作,但面向外部的 API 最终用户(在本例中开发人员是最终用户)被简化了。
我们喜欢简化:)
下一步
设计模式是软件工程师工具带中最强大的工具之一 - 如果
了解如何实现设计模式、何时采用以及在何处使用,对于领导团队的 JavaScript 工程师来说是非常重要的。在掌握这些设计模式的过程中,重构大师是一个非常好的参考资源。
本文中将展示100多个设计原则的实例,但实际上一篇文章无法涵盖所有可用的设计原则。因此,我强烈建议您将每周学习两个新的设计原理作为目标。一年后,您将掌握104条设计原则。作为软件工程师,您对任何团队和组织的价值都将成倍增加。
设计模式是可重用的面向对象软件开发元素。通过熟练掌握这些模式,您可以更好地领导团队并提高团队的开发效率。同时,不断学习和实践设计原则也将使您的技能得到提升,从而为团队创造更多的价值。