<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ 设计模式 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ 设计模式 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 26 May 2026 20:24:26 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/design-patterns/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ MVC 架构详解 ]]>
                </title>
                <description>
                    <![CDATA[ MVC 架构让复杂应用的开发过程变得更易于管理，它允许多个开发者协同开发。 第一次了解 MVC 模式时，我被这些术语吓到了，当我实际运用这些概念时更是如此。 回过头去，理解了 MVC 的含义以及作用，就能更轻松地将它运用于 web 应用的开发。 什么是 MVC MVC，即 model-view-controller，其中每个组件的含义如下：  * 模型（Model）：后端，包含了所有数据逻辑  * 视图（View）：前端界面或 GUI  * 控制器（Controller）：应用的大脑，控制数据如何展示 MVC 的概念最早由 Trygve Reenskaug 提出，他提出将其作为一种开发桌面应用 GUI 的方式。 如今 MVC 被用于现代 web 应用开发，因为它增强了应用的灵活性、可维护性和可扩展性。 为什么要用 MVC？ 五个字：关注点分离（separation of concerns，缩写为 SoC）。 MVC 模式有助于将前端和后端代码拆分为独立的组件，这样更便于管理，而且能够更方便地改动其中的某一部分而不会互相影响。 不过说起来容易做起来难，尤其是多个开发 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/the-model-view-controller-pattern-mvc-architecture-and-frameworks-explained/</link>
                <guid isPermaLink="false">6098ef4b0998fd05ae8c87ab</guid>
                
                    <category>
                        <![CDATA[ 设计模式 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Humilitas ]]>
                </dc:creator>
                <pubDate>Mon, 10 May 2021 08:43:15 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/05/BG.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>MVC 架构让复杂应用的开发过程变得更易于管理，它允许多个开发者协同开发。</p>
<p>第一次了解 MVC 模式时，我被这些术语吓到了，当我实际运用这些概念时更是如此。</p>
<p>回过头去，理解了 MVC 的含义以及作用，就能更轻松地将它运用于 web 应用的开发。</p>
<h2 id="mvc">什么是 MVC</h2>
<p>MVC，即 model-view-controller，其中每个组件的含义如下：</p>
<ul>
<li><strong>模型（Model）</strong>：后端，包含了所有数据逻辑</li>
<li><strong>视图（View）</strong>：前端界面或 GUI</li>
<li><strong>控制器（Controller）</strong>：应用的大脑，控制数据如何展示</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/MVC3.png" alt="MVC3" width="600" height="400" loading="lazy"></p>
<p>MVC 的概念最早由 Trygve Reenskaug 提出，他提出将其作为一种开发桌面应用 GUI 的方式。</p>
<p>如今 MVC 被用于现代 web 应用开发，因为它增强了应用的灵活性、可维护性和可扩展性。</p>
<h2 id="mvc">为什么要用 MVC？</h2>
<p>五个字：<strong>关注点分离（separation of concerns，缩写为 SoC）</strong>。</p>
<p>MVC 模式有助于将前端和后端代码拆分为独立的组件，这样更便于管理，而且能够更方便地改动其中的某一部分而不会互相影响。</p>
<p>不过说起来容易做起来难，尤其是多个开发者同时更新、修改或调试一个成熟应用时。</p>
<h2 id="mvc">如何使用 MVC</h2>
<p>为了更好地说明 MVC 模式，我引入了一个 web 应用，它展示了这些概念是如何工作的。</p>
<p>我的 Car Clicker 应用是著名的 Cat Clicker 应用的变体。</p>
<p>我的应用主要有以下区别：</p>
<ol>
<li>没有猫咪，<strong>只有</strong>性能车的图片（对不住了，爱猫人士！）</li>
<li>列出了多种车型</li>
<li>有多个点击计数器</li>
<li>只展示选中的车</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screen-Recording-2021-04-11-at-11.31.27.07-PM.gif" alt="Screen-Recording-2021-04-11-at-11.31.27.07-PM" width="600" height="400" loading="lazy"></p>
<p>让我们深入了解一下构成 MVC 架构模式的三个组件。</p>
<h3 id="">模型（数据）</h3>
<p>模型的任务是管理数据。不论数据是来自数据库、API 还是 JSON 对象，模型都要负责管理它们。</p>
<p>在 Car Clicker 应用中，模型对象包含一个由 car 对象组成的数组，其中含有这个应用所需的所有信息（数据）。</p>
<p>它还通过一个初始值为 <code>null</code> 的变量 <code>currentCar</code> 控制当前展示哪个汽车。</p>
<pre><code class="language-javaScript">const model = {
    currentCar: null,
    cars: [
        {
            clickCount: 0,
            name: 'Coupe Maserati',
            imgSrc: 'img/black-convertible-coupe.jpg',
        },
        {
            clickCount: 0,
            name: 'Camaro SS 1LE',
            imgSrc: 'img/chevrolet-camaro.jpg',
        },
        {
            clickCount: 0,
            name: 'Dodger Charger 1970',
            imgSrc: 'img/dodge-charger.jpg',
        },
        {
            clickCount: 0,
            name: 'Ford Mustang 1966',
            imgSrc: 'img/ford-mustang.jpg',
        },
        {
            clickCount: 0,
            name: '190 SL Roadster 1962',
            imgSrc: 'img/mercedes-benz.jpg',
        },
    ],
};
</code></pre>
<h3 id="ui">视图（UI）</h3>
<p>视图决定了用户看到的内容以及交互方式。</p>
<p>Car Clicker 应用有两个视图：<code>carListView</code> 和 <code>CarView</code>。</p>
<p>每个视图都有两个关键函数，定义其如何初始化及如何渲染。</p>
<p>这些函数决定了用户将会看到的内容以及交互方式。</p>
<h4 id="carlistview">carListView</h4>
<pre><code class="language-js">const carListView = {
    init() {
        // 保存 DOM 元素，方便后续访问
        this.carListElem = document.getElementById('car-list');

        // 渲染视图（使用正确的数据更新 DOM 元素）
        this.render();
    },

    render() {
        let car;
        let elem;
        let i;
        // 从控制器中获取待展示的汽车
        const cars = controller.getCars();

        // 确保渲染前列表是空的
        this.carListElem.innerHTML = '';

        // 遍历 cars 数组
        for(let i = 0; i &lt; cars.length; i++) {
            // 当前遍历到的车
            car = cars[i];

            // 创建一个汽车列表项（&lt;li&gt;）并设置其文本
            elem = document.createElement('li');
            elem.className = 'list-group-item d-flex justify-content-between lh-condensed';
            elem.style.cursor = 'pointer';
            elem.textContent = car.name;
            elem.addEventListener(
                'click',
                (function(carCopy) {
                    return function() {
                        controller.setCurrentCar(carCopy);
                        carView.render();
                    };
                })(car)
            );
            // 最后将其加入列表
            this.carListElem.appendChild(elem);
        }
    },
};
</code></pre>
<h4 id="carview">CarView</h4>
<pre><code class="language-js">const carView = {
    init() {
        // 保存 DOM 元素指针，方便后续访问
        this.carElem = document.getElementById('car');
        this.carNameElem = document.getElementById('car-name');
        this.carImageElem = document.getElementById('car-img');
        this.countElem = document.getElementById('car-count');
        this.elCount = document.getElementById('elCount');


        // 点击时增加当前汽车的计数
        this.carImageElem.addEventListener('click', this.handleClick);

        // 渲染视图（使用正确的数据更新 DOM 元素）
        this.render();
    },

    handleClick() {
    	return controller.incrementCounter();
    },

    render() {
        // 使用当前汽车的数据更新 DOM 元素
        const currentCar = controller.getCurrentCar();
        this.countElem.textContent = currentCar.clickCount;
        this.carNameElem.textContent = currentCar.name;
        this.carImageElem.src = currentCar.imgSrc;
        this.carImageElem.style.cursor = 'pointer';
    },
};
</code></pre>
<h3 id="">控制器（大脑）</h3>
<p>控制器负责获取数据、修改数据，并为用户提供数据。本质上，控制器就是视图和模型之间的链接。</p>
<p>通过 getter 和 setter 函数，控制器从模型拉取数据并初始化视图。</p>
<p>如果视图要更新后台数据，它会通过 setter 函数来修改数据。</p>
<pre><code class="language-js">const controller = {
    init() {
        // 将当前展示的车设为列表中的第一辆车
        model.currentCar = model.cars[0];

        // 初始化视图
        carListView.init();
        carView.init();
    },

    getCurrentCar() {
    	return model.currentCar;
    },

    getCars() {
    	return model.cars;
    },

    // 把“当前选中汽车”设为传入的对象
    setCurrentCar(car) {
    	model.currentCar = car;
    },

    // 增加当前选中汽车的计数
    incrementCounter() {
        model.currentCar.clickCount++;
        carView.render();
    },
};

// Let's goooo!
controller.init();
</code></pre>
<h2 id="mvc">MVC 框架</h2>
<p>JavaScript 越来越受欢迎，近年来还接管了后端。越来越多成熟的 JavaScript 应用选择了 MVC 架构模式。</p>
<p>框架来来去去，但是 MVC 架构模式的概念是不变的。</p>
<p>运用了这些概念的早期框架包括：<strong>KnockoutJS</strong>、<strong>Django</strong>、<strong>Ruby on Rails</strong>。</p>
<h2 id="">总结</h2>
<p>MVC 模式最吸引人的概念是关注点分离。</p>
<p>现代 web 应用非常复杂，有时做一些修改会令人很头疼。</p>
<p>将前端和后端作为独立的小组件来管理，可以使应用更灵活、更易于维护和扩展。</p>
<p><strong>如果你想了解这个 Car Clicker 应用，可以查看<a href="https://github.com/RafaelDavisH/car-clicker/blob/main/README.md">源码</a>或体验<a href="https://rafaeldavish.github.io/car-clicker/">在线版本</a>。</strong></p>
<p>🌟感谢你的阅读！🌟</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/the-model-view-controller-pattern-mvc-architecture-and-frameworks-explained/">The Model View Controller Pattern – MVC Architecture and Frameworks Explained</a>，作者：<a href="https://www.freecodecamp.org/news/author/rafaeldavish/">Rafael D. Hernandez</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 所有开发者都应该知道的基础设计模式 ]]>
                </title>
                <description>
                    <![CDATA[ 设计模式是针对我们软件工程师经常遇到的问题的设计级别解决方案。它不是代码，我再说一遍，它不是代码，而是描述如何解决这些问题以及设计解决方案。 我们认为使用这些模式是一种很好的实践，因为在设计解决方案的过程中进行了充分的尝试和测试，从而使最终代码具有更高的可读性。面向对象的程序语言（如 Java）经常创建和使用设计模式。接下来的大多数例子将会以 Java 编写。 设计模式的类型 目前发现了大约26种模式（我几乎不认为我会完成所有这些模式……）。 这26种可分为3种类型：  1. 创建型：这些模式是为类实例化而设计的。它们可以是类创建模式或对象创建模式。            2. 结构型：这些模式是根据类的结构和组成设计的。大多数这些模式的主要目标是增加所涉及类的功能，而不改变其组成。            3. 行为型：这些模式的设计取决于一个类如何与其他类通信。           在这篇文章中，我们将针对每种分类类型介绍一种基本设计模式。 类型1：创建型 - ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/the-basic-design-patterns-all-developers-need-to-know/</link>
                <guid isPermaLink="false">63476e0e31b59b077bbb1b69</guid>
                
                    <category>
                        <![CDATA[ 设计模式 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp.org ]]>
                </dc:creator>
                <pubDate>Mon, 13 Jan 2020 01:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/10/design-patterns-everywhere.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>设计模式是针对我们软件工程师经常遇到的问题的设计级别解决方案。它不是代码，我再说一遍，它<strong>不是代码</strong>，而是描述如何解决这些问题以及设计解决方案。</p>
<p>我们认为使用这些模式是一种很好的实践，因为在设计解决方案的过程中进行了充分的尝试和测试，从而使最终代码具有更高的可读性。面向对象的程序语言（如 Java）经常创建和使用设计模式。接下来的大多数例子将会以 Java 编写。</p>
<h1 id="">设计模式的类型</h1>
<p>目前发现了大约26种模式（我几乎不认为我会完成所有这些模式……）。</p>
<p>这26种可分为3种类型：</p>
<ol>
<li>
<p>创建型：这些模式是为类实例化而设计的。它们可以是类创建模式或对象创建模式。</p>
</li>
<li>
<p>结构型：这些模式是根据类的结构和组成设计的。大多数这些模式的主要目标是增加所涉及类的功能，而不改变其组成。</p>
</li>
<li>
<p>行为型：这些模式的设计取决于一个类如何与其他类通信。</p>
</li>
</ol>
<p>在这篇文章中，我们将针对每种分类类型介绍一种基本设计模式。</p>
<h2 id="1">类型1：创建型 - 单例设计模式</h2>
<p>单例设计模式是一种创建型模式，它的目标是限制一个类只能创建一个实例对象，并且只为该实例对象提供唯一一个全局访问点。Java 中这种类的一个常用示例是 Calendar ，它使用自己的 <code>getInstance()</code> 方法来获取要使用的实例对象，你无法为 Calendar 类再创建一个实例对象。</p>
<p>使用单例设计模式的类将包括</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/singleton-class-diagram.png" alt="单例类图" width="600" height="400" loading="lazy"></p>
<ol>
<li>一个私有静态变量，保存该类的唯一实例。</li>
<li>私有构造函数，因此无法在该类以外实例化。</li>
<li>一个公共静态方法，用于返回类的唯一实例。</li>
</ol>
<p>单例设计有许多不同的实现，今天我会介绍以下几种</p>
<ol>
<li>饿汉式实例化</li>
<li>懒汉式实例化</li>
<li>线程安全实例化</li>
</ol>
<h3 id="">雄心勃勃？</h3>
<pre><code class="language-java">public class EagerSingleton {
	// 创建类实例
	private static EagerSingleton instance = new EagerSingleton();

	// 私有构造器，因此无法在这个类的外部实例化
	private EagerSingleton() {  }

	// 获取创建的唯一实例对象。
	public static EagerSingleton getInstance() {
		return instance;
	}
}
</code></pre>
<p>这种类型的实例化发生在类加载期间，所以变量实例的实例化发生在任何方法之外。如果客户端应用程序根本没有使用此类，就会构成一个很大的缺陷。如果没有使用这个类，应急计划就是使用<strong>懒汉式实例化</strong></p>
<h3 id="">懒散的日子？</h3>
<p>这里与上述实现没有太大区别。主要区别在于静态变量被声明为 <code>null</code>，当且仅当实例变量在检查时仍然为 <code>null</code> 才在 <code>getInstance()</code> 方法中实例化。</p>
<pre><code class="language-java">public class LazySingleton {
	// 初始化实例置为null
	private static LazySingleton instance = null;

	// 私有构造器，因此无法在这个类的外部实例化
	private LazySingleton() {  }

	// 检查实例，若为null，则创建实例对象
	public static LazySingleton getInstance() {
		if (instance == null) {
			instance = new LazySingleton();
		}
		return instance;
	}
}
</code></pre>
<p>解决了一个问题，但另一个仍然存在。如果两个不同的客户端同时访问单例类，该怎么办呢？好吧，它们将同时检查实例是否为 <code>null</code>，并且会发现它们结果都是 <code>true</code>，所以会创建两个实例，两个客户端的每个请求各一个。要解决这个问题，需要实现<strong>线程安全实例化</strong>。</p>
<h3 id="">（线程）安全是关键？</h3>
<p>在 Java 中，关键字 <code>synchronized</code> 用于方法或对象以实现线程安全，因此同时只有一个线程能访问特定资源。类实例化被放在同步块中，所以该方法能在给定时间只由一个客户端访问。</p>
<pre><code class="language-java">public class ThreadSafeSingleton {
	// 初始化实例置为null
	private static ThreadSafeSingleton instance = null;

	// 私有构造器，因此无法在这个类的外部实例化
	private ThreadSafeSingleton() {  }

	// 检查实例，若为 null 且在同步块中，则创建实例对象
	public static ThreadSafeSingleton getInstance() {
		synchronized (ThreadSafeSingleton.class) {
			if (instance == null) {
				instance = new ThreadSafeSingleton();
			}
		}
		return instance;
	}
}
</code></pre>
<p>同步方法的开销很高，并降低了整个程序运行的性能。</p>
<p>举个例子，如果实例变量已经实例化，那么每次任何客户端访问 <code>getInstance()</code> 方法时，<code>synchronized</code> 方法都会运行并导致性能下降。所以 <code>synchronized</code> 方法只应该运行在检查实例变量的值为 <code>null</code> 时。如果检测结果为 <code>false</code>，就应该跳过 <code>synchronized</code> 方法。</p>
<p>为了减少此开销，使用双重锁。检查也在 <code>synchronized</code> 方法之前使用，如果值为 <code>null</code>，则运行 <code>synchronized</code> 方法。</p>
<pre><code class="language-java">// 双重锁用于降低 synchronized 方法的开销
public static ThreadSafeSingleton getInstanceDoubleLocking() {
	if (instance == null) {
		synchronized (ThreadSafeSingleton.class) {
			if (instance == null) {
				instance = new ThreadSafeSingleton();
			}
		}
	}
	return instance;
}
</code></pre>
<p>现在进入下一个分类。</p>
<h2 id="2">类型2：结构型 - 装饰器设计模式</h2>
<p>我将为你提供一个小场景，用来给出一个更好的上下文用来说明为什么以及在何处使用修饰器模式。</p>
<p>话说你拥有一家咖啡店，就像所有新手一样，刚开始你只需要家常咖啡和焦炒咖啡两种普通咖啡。在你的结算系统中，有一个类用于不同的混合咖啡，它继承了饮料抽象类。人们走进店里，准备喝你美妙（虽然苦涩）的咖啡。这时，有个咖啡新人想要加糖或牛奶，（很无语）真是对咖啡的嘲弄。</p>
<p>现在你需要将这两个配料放在菜单和结算系统上，你的IT人员别出心裁地为每种咖啡分别添加两个子类，一种加糖，一种加牛奶。这时，由于客户总是对的，其中一个又说这些可怕的话：</p>
<p>“请来一杯加糖的牛奶咖啡。”</p>
<h3 id="">???</h3>
<p>望着你的结算系统，尴尬而又不失礼貌的微笑再次挂在了你的脸上。 好吧，回到绘图板……</p>
<p>然后，IT人员将加糖的牛奶咖啡作为子类添加到每个父咖啡类中。本月剩下的时间一帆风顺，人们排队等候你的咖啡，你实际上赚钱了。</p>
<p>但等等，这还不够！</p>
<p>整个世界又怼你，一个竞争对手在街对面开业，不仅有4种咖啡，还有超过10种配料！</p>
<p>为了自己的咖啡卖得更好，对手有的没有的你都买了，这时候你想起了忘记更新那个讨厌的结算系统。你几乎不可能为所有的配料组合添加无穷无尽的子类，更不用说最终系统的大小。</p>
<p>是时候接入正确的结算系统了。你找到了懂行的IT人员，他们说：“如果使用装饰器模式，这将更容易和更小。”</p>
<h3 id="">装饰器模式究竟是什么？</h3>
<p>装饰器设计模式属于结构型，它处理类的实际结构，无论是继承，组合或是两者结合。它设计的目标是在运行时修改对象的功能，这也是许多其他设计模式的目的之一。</p>
<p>让我们给<strong>数学</strong>一个登场机会（不寒而栗？），来把这一切都带入视野：</p>
<p>4种混合咖啡和10种配料。如果我们坚持为每一种类型的咖啡的所有配料的每一种不同组合生成子类。那是：</p>
<p>(10–1)² = 9² = 81 个子类</p>
<p>我们从10中减去1，因为你不能将同一种配料混合，糖加糖听起来太蠢了，这只是一种混合咖啡。81乘以4，你会得到惊人的324个不同的子类！再想想那些涉及到的代码……</p>
<p>但是在这种情况下，装饰器模式只需要16个类。敢打赌吗？</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/decorator-class-diagram.png" alt="Decorator Design Pattern Class diagram" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/decorator-coffee-class-diagram.png" alt="Class diagram according to coffee shop scenario" width="600" height="400" loading="lazy"></p>
<p>如果我们根据上面的类图绘制出我们的场景，我们得到4个混合咖啡类，10个配料类，1个抽象组件，1个抽象装饰器。看！16个！现在交出100美元。(开玩笑别当真, 当然如果你愿意给，我也不会拒绝……嘛，说说而已)</p>
<p>从上面可以看出，正如实现混合咖啡类是饮料抽象类的子类一样，配料抽象类也从饮料抽象类继承了方法。配料类作为饮料抽象类的子类，能在需要时用自己的新方法向基础对象添加功能。</p>
<p>让我们来编码，看看这个模式如何运用。</p>
<p>首先制作抽象饮料类，所有不同的混合咖啡类将继承自饮料抽象类：</p>
<pre><code class="language-java">public abstract class Beverage {
	private String description;
    
	public Beverage(String description) {
		super();
		this.description = description;
	}
    
	public String getDescription() {
		return description;
	}
    
	public abstract double cost();
}
</code></pre>
<p>然后添加实现混合咖啡类</p>
<pre><code class="language-java">public class HouseBlend extends Beverage {
	public HouseBlend() {
		super(“House blend”);
	}

	@Override
	public double cost() {
		return 250;
	}
}

public class DarkRoast extends Beverage {
	public DarkRoast() {
		super(“Dark roast”);
	}

	@Override
	public double cost() {
		return 300;
	}
}
</code></pre>
<p>配料抽象类也继承自饮料抽象类（更多内容见下文）。</p>
<pre><code class="language-java">public abstract class AddOn extends Beverage {
	protected Beverage beverage;

	public AddOn(String description, Beverage bev) {
		super(description);
		this.beverage = bev;
	}

	public abstract String getDescription();
}
</code></pre>
<p>现在这个抽象类的具体实现类：</p>
<pre><code class="language-java">public class Sugar extends AddOn {
	public Sugar(Beverage bev) {
		super(“Sugar”, bev);
	}

	@Override
	public String getDescription() {
		return beverage.getDescription() + “ with Mocha”;
	}

	@Override
	public double cost() {
		return beverage.cost() + 50;
	}
}

public class Milk extends AddOn {
	public Milk(Beverage bev) {
		super(“Milk”, bev);
	}

	@Override
	public String getDescription() {
		return beverage.getDescription() + “ with Milk”;
	}

	@Override  public double cost() {
		return beverage.cost() + 100;
	}
}
</code></pre>
<p>如你所见，我们可以将饮料抽象类的任何子类传递给配料抽象类的任何子类，并且获得增加的成本以及更新描述。由于配料抽象类继承自饮料抽象类，我们可以将配料抽象类传递给另一个配料抽象类。这样，我们就可以为特定的混合咖啡添加任意数量的配料。</p>
<p>现在编写一些代码来测试它。</p>
<pre><code class="language-java">public class CoffeeShop {
	public static void main(String[] args) {
		HouseBlend houseblend = new HouseBlend();
		System.out.println(houseblend.getDescription() + “: “ + houseblend.cost());

		Milk milkAddOn = new Milk(houseblend);
		System.out.println(milkAddOn.getDescription() + “: “ + milkAddOn.cost());

		Sugar sugarAddOn = new Sugar(milkAddOn);
		System.out.println(sugarAddOn.getDescription() + “: “ + sugarAddOn.cost());
	}
}
</code></pre>
<p>最终结果是：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/decorator-final.PNG" alt="P.S. this is in Sri Lankan Rupees" width="600" height="400" loading="lazy"></p>
<p>运行成功！我们不需要为所有混合咖啡的每种配料组合添加无限多的子类，也能够在混合咖啡中添加多个配料，并成功更新其最终成本和描述。</p>
<p>来，看看最后一类。</p>
<h2 id="3">类型3：行为型 - 命令设计模式</h2>
<p>行为设计模式侧重于类、对象如何相互通信。命令模式的主要焦点是在所涉及的各个类之间灌输更高程度的低耦合。</p>
<p>呃……那是什么？</p>
<p>耦合是两个（或多个）类之间的交互方式。这些类交互时的理想情况是它们不会相互依赖，这被称为低耦合。所以，低耦合的良好定义是互相连接的类，彼此直接调用最少。</p>
<p>当需要发送请求而不需要明确知道请求者或接收者是谁时，就需要这种模式。</p>
<p>在这种模式中，调用类与实际执行操作的类分离。调用者类中只有在客户端请求时运行必要命令的可调用方法 <code>execute</code>。</p>
<p>我们来看一个现实世界中的基本例子——在一家高档餐厅点餐。随着流程的进行，你将订单（命令）交给服务员（调用者），然后服务员将其交给厨师（接收者），这样你就可以获得食物。可能听起来很简单，但对于代码实现就有点……</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/chain-of-command-be-like-pop-snoke-im-going-to-27790631.png" alt="The idea is pretty simple, but the coding goes around the nose." width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/command-class-diagram.PNG" alt="Command Design Pattern Class Diagram" width="600" height="400" loading="lazy"></p>
<p>技术方面的操作流程是，你点菜形成了一个订单（这个订单 <code>Order</code> 具体实现了命令接口要求厨师完成一道菜）。你把订单交个服务员（服务员 <code>Waiter</code> 就是调用者，他知道何时发出此命令）。服务员把订单交给厨师（收到特定订单时，厨师 <code>Chef</code> 是唯一知道具体该做什么的人，这就实现了低耦合）。如此，当调用者执行 <code>execute</code> 方法时，它调用订单对象的 <code>execute</code> 方法，而订单对象的 <code>execute</code> 方法又调用接收者对应的方法，从而完成必要的操作。</p>
<h3 id="">我们需要实现：</h3>
<ol>
<li>一个接口 <code>Command</code></li>
<li>一个具体实现 <code>Command</code> 接口的 <code>Order</code> 类</li>
<li>一个 <code>Waiter</code> 类（调用者）</li>
<li>一个 <code>Chef</code> 类（接收者）</li>
</ol>
<p>所以代码大概像这样：</p>
<h4 id="">厨师是接收者</h4>
<pre><code class="language-java">public class Chef {
	public void cookPasta() {
		System.out.println(“Chef is cooking Chicken Alfredo…”);
	}

	public void bakeCake() {
		System.out.println(“Chef is baking Chocolate Fudge Cake…”);
	}
}
</code></pre>
<h4 id="commandinterface">命令接口（ <code>Command Interface</code> ）</h4>
<pre><code class="language-java">public interface Command {
	public abstract void execute();
}
</code></pre>
<h4 id="">订单是命令接口具体实现类</h4>
<pre><code class="language-java">public class Order implements Command {
	private Chef chef;
	private String food;

	public Order(Chef chef, String food) {
		this.chef = chef;
		this.food = food;
	}

	@Override
	public void execute() {
		if (this.food.equals(“Pasta”)) {
			this.chef.cookPasta();
		} else {
			this.chef.bakeCake();
		}
	}
}
</code></pre>
<h4 id="">服务员是调用者</h4>
<pre><code class="language-java">public class Waiter {
	private Order order;

	public Waiter(Order ord) {
		this.order = ord;
	}

	public void execute() {
		this.order.execute();
	}
}
</code></pre>
<h4 id="">你是客户</h4>
<pre><code class="language-java">public class Client {
	public static void main(String[] args) {
		Chef chef = new Chef();
        
		Order order = new Order(chef, “Pasta”);
		Waiter waiter = new Waiter(order);
		waiter.execute();

		order = new Order(chef, “Cake”);
		waiter = new Waiter(order);
		waiter.execute();
	}
}
</code></pre>
<p>如上所示，客户发出订单并将接收者设置为厨师。该订单将发送给服务员，服务员知道何时执行订单（即何时向厨师下订单进行烹饪）。当调用者执行程序时，订单的执行方法在接收者上运行（即主厨被命令要么煮意大利面要么烘烤蛋糕）。</p>
<h1 id="">快速回顾一下</h1>
<p>在这篇文章中我们说明了：</p>
<ol>
<li>真正的设计模式是什么</li>
<li>不同类型的设计模式以及它们为什么不同</li>
<li>每种类型的一个基本或通用设计模式</li>
</ol>
<p>我希望这可以帮到你。</p>
<p>点击<a href="https://github.com/samsam-026/Design_Patterns">这里</a>跳转到这篇文章示例代码的仓库</p>
<hr>
<blockquote>
<p><a href="http://www.liuzhaocn.com/?p=1311">深入研究单例模式</a><br>
<a href="https://www.cnblogs.com/qq641120784/p/8038880.html">对象和实例的区别</a><br>
<a href="http://c.biancheng.net/view/1380.html">命令模式</a></p>
</blockquote>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
