什么是设计模式?您能说出工厂模式、抽象工厂模式、创建者模式、原型模式、原型模式的潜复制及深复制、单例模式、命令模式的原理吗?
简介
这是一个小巧的设计模式常见问题问答。在本节我们将一起探讨工厂模式、抽象工厂模式、创建者模式、原型模式、原型模式的浅复制及深复制、单例模式、命令模式的原理。
在下面的链接中您可以阅读设计模式常见问题问答的后续部分 :-
设计模式 FAQ’s 2 --- 解释器模式、迭代器模式、调停者模式、备忘录模式、观察者模式(http://www.codeproject.com/KB/aspnet/SoftArch2.aspx)
设计模式 FAQ’s 3 --- 状态模式、策略模式、访问者模式、适配器模式、享元模式 (http://www.codeproject.com/KB/aspnet/SoftArch3.aspx)
设计模式 FAQ’s 4 --- 桥接模式、组合模式、装饰器模式、门面模式、责任链模式、代理模式、模板模式( http://www.codeproject.com/KB/aspnet/SoftArch4.aspx)
什么是设计模式?
设计模式是给定的上下文中重复出现问题的解决方案。所以基本上一个解决方案能处理同一类型的问题。设计模式应该在软件开发的初始阶段以适当的形式存在。比如说,您想实现一个排序算法并首先想到了冒泡排序。所以问题就是如何实现排序,解决方案是冒泡排序。设计模式也是如此。
设计模式主要有哪三种分类?
设计模式有三种基本类别:创建型模式、结构型模式和行为型模式
创建型模式:
-
抽象工厂模式:为一组类创建实例。
-
建造者模式:将对象的构造和表现分离开来。
-
工厂方法模式:为多个派生类创建一个实例。
-
原型模式:一个完全初始化对象实例的拷贝或克隆。
-
单例模式:只有一个实例存在的类。
注意记住创建型模式最好的方式是谨记ABFPS (Abraham Became FirstPresident of States 亚伯拉罕成为国家第一任总统)
结构型模式
-
适配器模式:匹配不同接口的类。
-
桥接模式:将一个对象的抽象部分从实现中分离出来。
-
组合模式:树结构的简单和复合对象。
-
装饰器模式:为对象动态添加职责。
-
门面模式:一个代表整个系统的类。
-
享元模式:用来细粒度的高效共享实例。
-
代理模式:用一个对象表示另一个对象。
行为型模式
-
调停者模式:定义了简化类和类之间的交互。
-
备忘录模式:捕获和恢复一个对象的内部状态。
-
解释器模式:将语言元素包含在一个程序中的一种方式。
-
迭代器模式:按顺序访问集合中的元素。
-
职责链模式:在一连串对象中传递气球的一种方式。
-
命令模式:将一个请求封装成一个对象。
-
状态模式:当一个对象状态发生变化时选择该对象的一种行为。
-
策略模式:将一个算法封装到一个类之中。
-
观察者模式:一种通知若干类发生变化的机制。
-
模版方法模式:推迟具体步骤的算法到子类中。
-
访问者模式:在类不变的情况下定义一个新的操作。
你能否解释清楚工厂模式?
-
工厂模式属于创建型模式。这你可以从名字中“工厂”两字看得出来,它意味这构造或者创建一些东西。在软件架构的范畴工厂模式意味着集中创建一些对象。下面是客户不同类型的发票的代码片段,这些发票的创建依赖于客户端指定的发票类型。以下代码有两个问题:
-
首先,我们有很多“new”关键字散落在客户端。换句话说,客户端加载时有肯多创建对象的过程使得客户端逻辑变得非常复杂。
其次,客户端需要知道所有的发票类型。所以,如果我们我们要添加一种新的发票类型如“InvoiceWithFooter”,那么客户端需要引入新的类并且需要重新编译。
图1:不同类型的发票
以这些问题作为基础,现在我们将看看工厂模式如何帮我们解决问题。下图中“工厂模式”展示的了两个具体类‘ClsInvoiceWithHeader’和‘ClsInvoiceWithOutHeader’。
第一个问题是这些类直接和客户端有联系导致了客户端代码中散落了很多“new”关键字。这个问题通过引入一个新的类‘ClsFactoryInvoice’来做所有创建对象的工作来解决。
第二个问题是客户端代码知道所有具体类,如‘ClsInvoiceWithHeader’和‘ClsInvoiceWithOutHeader’。这导致了增加新的发票类型时需要重新编译客户端代码,例如如果增加‘ClsInvoiceWithFooter’类型的发票,客户端代码需要改变并重新编译。为了解决这个问题我们引入了一个通用接口‘IInvoice’,两个具体类‘ClsInvoiceWithHeader’和‘ClsInvoiceWithOutHeader’都继承并实现‘IInvoice’接口。
客户端只需要引用‘IInvoice’接口,这使得客户端和具体类(‘ClsInvoiceWithHeader’和‘ClsInvoiceWithOutHeader’)0关联。所以现在当我们再去添加新的发票类时客户端不需要做任何改动。
在创建对象的那行只需要关注‘ClsFactoryInvoice’即可,这样客户端通过只关注‘IInvoice’接口取消了与具体类的关联。
图2:工厂模式
以下是用C#实现的工厂模式代码片段。为了避免重新编译客户端我们引入了‘IInvoice’发票接口。‘ClsInvoiceWithOutHeaders’和‘ClsInvoiceWithHeader’都实现该接口。
图3:接口和具体类
我们还引入了一个额外的包含getInvoice()方法的类‘ClsFactoryInvoice’,该类会根据发票类型来生成发票对象。简而言之,我们将创建对象的逻辑集中在了‘ClsFactoryInvoice’中;客户端只需要调用‘getInvoice’方法就可以生成发票对象。其中最重要的一点需要注意的是客户端只引用‘IInvoice’类型并且工厂类‘ClsFactoryInvoice’同样只返回‘IInvoice’类型的引用。这有助于客户端从具体类中解耦,因此,当我们新加发票类型对应的类时不需要重新编译客户端。
图4:生成发票的工厂类
你能否解释清楚抽象工厂模式?
抽象工厂扩展了基本工厂模式。抽象工厂模式帮助我们将相似的工厂模式类集中到一个统一的接口下。所以现在基本上所有常见的工厂模式都继承自一个通用的抽象工厂类来将他们统一到一个共同类中。其他和工厂模式相关的东西都和之前讨论的一样。
一个工厂类帮助我们将类的创建和类型集中起来。抽象工厂模式帮助我们形成相关工厂模式之间的一致性从而为客户端提供更简单的接口。
图5:抽象工厂结合相关工厂模式
现在既然我们已经知道基本要素那么让我们尝试来理解抽象工厂模式的实现细节。如之前所说,我们通过继承将工厂模式类(factory1和factory2)绑定至一个共同的抽象工厂(AbstractFactory接口)。工厂类位于具体类之上,再次从共同接口中推导。例如图“实现抽象工厂”中具体类‘product1’和‘product2’都继承自同一个接口,如‘common’。而需要使用具体类的客户端只需要和抽象工厂以及具体类所实现的抽象接口交互即可。
图6:实现抽象工厂
现在我们来看看如何在实际代码中实现抽象工厂模式。我们有一个场景是在ui对象创建中我们需要通过文本框(textbox)和按钮(button)各自的中心化工厂类‘ClsFactoryButton’和‘ClsFactoryText’来创建各自对象。这两个类都继承自同一个接口‘InterfaceRender’;并且两个工厂类都继承自共同的工厂类‘ClsAbstractFactory’。图“抽象工厂例子”中展示了这类的关系以及客户端代码是相同的。其中重要的一点是客户端代码不需要与具体类交互。对于对象的创建,客户端使用抽象工厂类(ClsAbstractFactory),而调用具体类则通过调用接口‘InterfaceRender’中的共用方法来完成。因此‘ClsAbstractFactory’类为‘ClsFactoryButton’和‘ClsFactoryText’的工厂类提供了共同的抽象接口。
图7:抽象工厂例子
下面我们将运行例子中抽象工厂的代码。下图“抽象工厂和工厂代码快照”中的代码快照展示的工厂模式类与抽象工厂的继承关系。
图8:抽象工厂和工厂代码快照
图“具体类通用接口”展示了具体类和通用接口‘InterFaceRender’的继承关系,其中‘InterFaceRender’接口为所有具体类强制增加‘render’方法。
图9:通用接口
最后要做的事情就是客户端通过使用‘InterfaceRender’接口和‘ClsAbstractFactory’抽象工厂来调用和创建对象。其中关键要素是该代码完全和具体类隔离。所以任何具体类的变化如增加或删除具体类都不会引起客户端改变。
图10:客户端、接口、抽象工厂
你能否解释清楚创建者模式?
创建者模式属于创建型模式分类。创建者模式帮助我们将对象的复杂构造从其表现中分离开来,所以相同的构造处理过程可以创建出不同的表现行为。当一个对象的构造函数非常复杂时创建模式非常有用;该模式主要目的就是将对象的构造从其表现中分离。如果我们可以将对象的构造与行为分离,我们就可以了从相同的构造中获得许多不同行为的对象。
图11:创建者模式概念
为了理解我们所说的构造和行为,我们举个“准备茶”的例子。 我们可以从图“泡茶”中看到,从相同的泡茶步骤中我们可以得到3种不同表现的茶(如不含糖的茶、含糖或牛奶的茶、不含牛奶的茶)。
图12:泡茶
现在让我们来举个软件方面的例子来看看如何将对象复杂的创建和它的表现分开。假设我们有一个应用,我们需要将同一个报表显示为pdf和excel格式。图“请求报表”展示了一系列步骤来达到同一目的的流程。传入需要创建的报表类型、设置报表头和脚、最终报表得以展示。
图13:请求报表
接下来让我们从下图“不同视角”中从不同视角来再次看该问题。图“请求报表”中定义的相同流程被分解为表现和通用构造。其中构造过程对不同类型的报表处理都是相同的,但是他们的表现形式却各不相同。
还是这个报表问题,我们尝试使用创建者模式来解决该问题。实现创建者模式分为以下3个主要部分:
-
Builder:主要负责定义各个部分的处理过程。Builder将各个处理过程初始化并配置到产品中。
-
Director:负责取到各个处理过程并定义构建产品的流程。
-
Product:该对象为builder和director协作创建而成的最终对象。
下面让我们来看下创建者模式的类继承关系。我们从自定义创建者(builder)如‘ReportPDF’、‘ReportEXCEL’中抽象出类‘ReportBuilder’。
图14:创建者类继承关系
下图“创建者类代码”展示了这些类的方法。为了生成报表我们首先需要创建一个报表对象,接下来设置报表类型(EXCEL或者PDF)、设置表头、设置页脚,最终返回报表。目前我们定义了两个自定义创建者,一个是‘PDF’(ReportPDF)另一个为‘EXCEL’(ReportExcel)。这两个自定义创建者都根据报表类型定义有自身的处理过程。
图15:创建者代码
接下来我们来了解director是如何工作的。‘clsDirector’持有builder并按顺序调用各个方法处理。因此director就行司机一样持有所有单个处理过程并按照一定顺序来调用他们来生成最终产品;在这个例子中的体现就是报表的生成。下图“Director实践”展示了方法‘MakeReport’调用各个处理来生成PDF或EXCEL格式的报表。
图16:Director实践
创建者模式中第三部分组件就是产品(product),它在我们的例子中就仅仅是表示报表类。
图17:报表类
现在让我们来看一下创建者项目的完整视图。图“Client,builder,director以及product”展示了这些对象是如何构成创建者模式的。客户端(client)创建director类对象并将恰当的创建者(builder)传递给产品(product)来初始化。产品依赖于创建者被初始化或创建并最终传递给客户端。
图18:Client,builder,director以及product
输出的加过就像这样。我们可以看到两种类型的报表根据不同的构建者显示各自的报表头。
图19:创建者最终输出结果
你能否解释清楚原型模式?
原型模式也属于创建型模式。它提供一种方式来为已经存在的对象实例创建新的对象。一种方式是我们克隆已存在对象及其数据;通过这种方式,对克隆对象的任何修改都不影响原对象。如果你想仅仅通过设置原对象就可以修改克隆对象,那么就错了。想要通过将一个对象设置到另一个对象,我们需要传址到对象引用;因此修改新对象也会影响原对象。为了更清楚的理解传址(“BYREF”)请考虑下图“传址”。一下是代码流程:
-
第一步创建第一个对象,如class1的对象obj1
-
第二步创建第二个对象,如class1的对象ojb2
-
第三步设置旧的对象的值,如obj1设置为“old value”
-
第四步将obj1设置到obj2
-
第五步改变obj2的值
-
现在我们显示两个对象的值会发现两个对象的值都被改变
图20:传址
以上例子可得出结论一个对象设置为另一个对象为传地址。因此改变新对象值的同时也会改变原对象的值。
然而有很多实例我们希望复制对象值的改变不影响原对象。那么最佳选择就是原型模式。
下面我们看个c#的例子。下图“原型模式实践”中自定义了类‘ClsCustomer’需要克隆。在C#中可以使用‘MemberWiseClone’方法来实现。Java中则可以通过‘Clone’方法来达到目的。同样在代码中我们还展示了客户端代码,我们创建了自定义类的对象‘obj1’和‘obj2’。任何对obj2的改变都不会影响obj1,因为obj2是obj1的完全克隆复制。
图21:原型模式实践
你能否解释清楚原型模式中的浅拷贝和深拷贝?
原型模式有两种克隆方式。一种是浅拷贝,如以上例子中所示;在浅拷贝中只是拷贝对象,该对象的所包含的其他东西都不克隆;例如下图中“深克隆实践”我们自定义了一个类并且在该类中聚合了一个地址类。‘MemberWiseClone’则只会克隆自定义类‘ClsCustomer’而不会拷贝‘ClsAddress’类。因此我们也为地址类增加‘MemberWiseClone’功能。这样当调用‘getClone’功能时我们同时调用了父类和子类的克隆方法,这使得对象可以被完全拷贝。当父类对象和它们所包含的对象都被克隆时我们称之为深克隆;当只有父类对象被拷贝时我们称之为浅拷贝。
图22:深拷贝实践
你能否解释清楚单例模式?
重点:为一个对象创建唯一实例并通过一个统一的点来获取该唯一实例。
在项目中有这样一种场景,我们需要某个对象只创建一个实例并在客户端之间共享。例如,假设我们有连个类currency和country。
这些类加载主要数据并且会被在项目中一次又一次被引用,但是我们想只有一个共享实例来提升性能而不是一次又一次连接查询数据库。
实现单例模式有4个步骤:
步骤1:创建私有构造函数的封闭类
私有构造函数非常重要,因为客户端不能通过该类直接创建对象。本节中重点所示,这种模式的主要目的是为对象创建一个唯一的可以全局共享的实例,因此我们不希望给客户端直接创建该对象的权利。
步骤2:创建只需要一个对象类的对象(在这个例子中为currency和country)
步骤3:为该类创建一个静态只读对象并同样通过静态属性暴露出来,如下所示:
步骤4:现在可以通过以下代码在客户端使用该单例对象
以下是我们上边讨论的单例模式完整代码:
你能否解释清楚命令模式?
命令模式允许请求以对象的方式存在。下面我们来解释具体含义。下图“菜单及命令”中展示了点击不同菜单有不同操作的例子。因此,点击不同的菜单我们传递一个action中所包含的字符串;通过该action字符串我们执行不同的action。而以下代码中不好的一点是其中有很多if条件语句使得代码不够清晰。
图23:菜单及命令
命令模式将以上action移到对象中,由这些对象来执行真正的命令。
如之前所说,每个命令是一个对象,我们首先准备为每个命令准备各个类,如exit,open,file以及print等。以上所有action都被包装在类中如退出action被包装在‘clsExecuteExit’中,打开被包装在‘clsExecuteOpen’,print被包装在‘clsExecutePrint’中等等。说有这些类都继承自‘IExecute’接口。
图24:对象及命令
我们可以创建inovker来使用所有action类。invoker的主要工作就是封装对action的调用并持有所有action类。
这样我们要将所有actiont添加到一个集合中如ArrayList。同时我们还开放了一个方法‘getCommand’来接收一个字符串来返回抽象的‘IExecute’对象。到此,客户端代码就边的直接而灵活了;所有的if语句都被移动至‘clsInvoker’类。
图25:调用者及客户端代码
1. 本文由程序员学架构翻译
2. 本文译自
http://www.codeproject.com/Articles/28309/Design-pattern-FAQ-Part-Training
3. 转载请务必注明本文出自:程序员学架构(微信号:archleaner )
4. 更多文章请扫码:
相关推荐
这个压缩包文件“设计模式问答(2)Java开发Java经验技巧共18页.pdf.zip”显然包含了关于Java开发中的设计模式及其应用的深入讨论。下面,我们将详细探讨这些设计模式以及它们在Java开发中的重要性。 1. **单例模式...
JAVA设计模式问答题[整理].pdf
为了更好地实现这些目标,设计师们常常借鉴一系列成熟的UI设计模式。本文将详细介绍几种常见的UI设计模式及其应用场景,帮助设计师们更好地理解和运用这些模式。 #### 二、常见UI设计模式详解 ##### 1. 主体/细节...
从提供的文件信息中,我们可以推断该课件《C++设计模式课件25_Interpreter.pdf》是关于C++语言实现设计模式中的解释器模式(Interpreter Pattern)的一部分教学资料。文件中出现的网站***和网易云课堂是指两个在线...
设计模式是软件工程中的一种重要概念,用于解决在软件设计中常见的问题,提供了一套可重用的解决方案。设计模式的出现使得开发者能够更高效地编写可维护、可扩展的代码,遵循一定的设计原则,提高代码质量。以下是...
《JAVA设计模式》期末试题(A卷)主要涵盖了设计模式的基础知识和应用,包括选择题、填空题、名词解释和综合问答四个部分,涉及到了多种设计模式的概念、分类、作用以及Java语言中与设计模式相关的编程原则。...
本书的特色是通过小菜与大鸟的趣味问答,在讲解程序的不断重构和演变过程中,把设计模式的学习门槛降低,让初学者可以更加容易地理解——为什么这样设计才是好的?是怎样想到这样设计的?以达到不但授之以“鱼”,还...
UML设计模式笔试题 UML 设计模式笔试题是 Java 程序设计的重要组成部分,本文档涵盖了 UML 设计模式、Java 基础知识、多线程编程、Struts2 框架、MVC 模式、设计模式等多方面的知识点。 一、选择题 1. UML 设计...
以C语言程序设计教学为例,构建教学领域的知识图谱,可以辅助智能问答机器人更有效地理解和解答课程相关问题。机器人通过对问题的分析、信息检索和答案抽取,为学生提供及时的解答。同时,通过对大量问答日志的分析...
问答是happy http://blog.csdn.net/b275518834/article/details/8247685 操作方式:输入文本框设置线程数 点击第一个按钮请求10个地址信息 点击第二个按钮中断10个地址信息 1:判断当前网络环境 2:编写了3套...
目录 第一部分 人工智能大数据概览 第二部分 知识图谱技术概览 第三部分 自动问答解决方案 人工智能自动问答系统方案设计全文共33页,当前为第2页。 我国的大数据+人工智能战略 国外 2016年初,AlphaGo在围棋领域...
1. **MVC设计模式**:Model-View-Controller模式是Web应用开发中常用的设计模式,它将业务逻辑、数据模型和用户界面分离,使得代码更易于维护和扩展。 2. **Servlet与JSP**:Servlet用于处理HTTP请求,JSP则用于...
2. **MVC架构**:SpringBoot支持Model-View-Controller(MVC)设计模式,用于处理Web请求。在问答系统中,模型代表数据模型,视图负责展示,控制器接收并处理用户请求,协调模型和视图的交互。 3. **Spring Data ...
本文将深入探讨“10000条保险问答数据集”这一资源,该数据集提供了丰富的信息,为保险行业的数据分析、问答系统构建以及数据挖掘提供宝贵材料。 首先,我们要理解这个数据集的基本构成。它包含了一个名为“10000条...
《基于知识图谱的问答系统综述》这篇文章探讨了如何在知识库中进行问答系统的设计与实现。随着知识图谱的日益发展,如何有效理解和利用这些丰富的知识来生成恰当的答案成为一个挑战。知识图谱(Knowledge Graphs)是...
总之,【JAVA知识问答器】是一个综合运用了Java核心特性和设计模式的项目,通过它不仅可以学习到Java的基础语法,还可以深入理解面向对象设计、资源管理以及数据持久化等方面的知识。对于初学者来说,这是一个极好的...
问答系统是互联网上常见的信息交流工具,它允许用户提出问题并由其他用户或专家给出回答,类似于现实生活中的一种社区互助模式。 【标签】"问答系统11111"强调了这个主题的核心——问答系统,可能是为了突出其在...
这种设计模式是典型的C/S(客户端/服务器)架构,其中服务器端是服务的提供者,客户端是服务的消费者。 在描述中提到了“接口”,这可能是指Java中的接口(Interface)。在Java中,接口是一种完全抽象的类型,定义...
总结,基于epoll的服务器设计充分利用了epoll的高效特性,实现了推送和问答模式的结合,是学习网络编程和并发处理的优秀实践案例。通过理解和掌握这个项目,开发者能够更好地理解epoll的工作原理以及如何利用它构建...