`
冰糖葫芦
  • 浏览: 293272 次
社区版块
存档分类
最新评论

设计模式问答(一)

阅读更多

什么是设计模式?您能说出工厂模式、抽象工厂模式、创建者模式、原型模式、原型模式的潜复制及深复制、单例模式、命令模式的原理吗?

简介

这是一个小巧的设计模式常见问题问答。在本节我们将一起探讨工厂模式、抽象工厂模式、创建者模式、原型模式、原型模式的浅复制及深复制、单例模式、命令模式的原理。

在下面的链接中您可以阅读设计模式常见问题问答的后续部分 :-

设计模式 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. 更多文章请扫码:


分享到:
评论

相关推荐

    JAVA设计模式问答题[整理].pdf

    JAVA设计模式问答题[整理].pdf

    设计模式问答(2)Java开发Java经验技巧共18页.p

    设计模式问答(2)Java开发Java经验技巧共18页.pdf.zip

    大话设计模式的源码 pdf文件在我空间免费下载

    本书的特色是通过小菜与大鸟的趣味问答,在讲解程序的不断重构和演变过程中,把设计模式的学习门槛降低,让初学者可以更加容易地理解——为什么这样设计才是好的?是怎样想到这样设计的?以达到不但授之以“鱼”,还...

    android 网络应用轻量框架-多线程管理-高效缓存-设计模式

    问答是happy http://blog.csdn.net/b275518834/article/details/8247685 操作方式:输入文本框设置线程数 点击第一个按钮请求10个地址信息 点击第二个按钮中断10个地址信息 1:判断当前网络环境 2:编写了3套...

    人工智能自动问答系统方案设计.pptx

    目录 第一部分 人工智能大数据概览 第二部分 知识图谱技术概览 第三部分 自动问答解决方案 人工智能自动问答系统方案设计全文共33页,当前为第2页。 我国的大数据+人工智能战略 国外 2016年初,AlphaGo在围棋领域...

    【毕业设计】基于深度学习的视觉问答.zip

    研究目的 对于视觉问答(VQA)的研究具有深刻的学术意义和广阔的应用前景。...根据每个问题中对应答案出现的次数来计算每个答案的分数,然后将每个问题的答案形成一个一维的分布(候选答案总数为3097)

    基于Java的A校招生咨询智能问答系统的设计与实现【附源码】

    A校招生咨询智能问答系统主要功能模块包括资讯数据管理、用户注册管理、留言管理、网上咨询管理、智能问答、系统管理,采取面对对象的开发模式进行软件的开发和硬体的架设,能很好的满足实际使用的需求,完善了对应...

    WeCenter社交化问答系统 v3.1.9社交问答 问答系统 问答程序

    Wecenter(微中心系统软件)是一款由深圳市微客互动有限公司开发的具有完全自主知识产权的开源软件。它安全,可靠,快速更迭,可以迅速帮助企业和组织通过微信,微薄,APP,网页...Web 版模板设计调整 手机版模板重构

    问答类APP创业策划书.zip

    在这份策划书中,我们可以看到一个精心设计的问答类APP的蓝图。它立足于解决现代人在日常生活、工作、学习中遇到的各种问题,通过智能匹配与高效互动,为用户提供一个便捷、准确的问答平台。这个平台不仅可以帮助...

    问答系统的系统设计方案.pdf

    问答系统的系统设计⽅案 问答系统的系统设计⽅案 问答系统的系统设计⽅案 问答系统的系统设计⽅案 ⼀、软件架构风格 ⼀、软件架构风格 "每⼀个模式描述了⼀个在我们周围不断重复发⽣的问题及该问题解决⽅案的核⼼。...

    软件工程问答题.docx

    1软件是设计开发的,而不是传统意义上生产制造的。2软件不会"磨损"。 3虽然整个工业向着基于构件的构造模式发展,然而大多数软件仍是根据实际的顾客需求定制的。 What is equivalence partitioning as it applies ...

    人工智能问答.docx

    人工智能问答全文共2页,当前为第1页。人工智能问答全文共2页,当前为第1页。...模式识别16数据挖掘与数据库中的知识发现17.计算机辅助创新18.计算机文艺创作19.机器博弈20.智能机器人 人工智能问答

    asp.net知识库

    深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托...

    图像问答系统_java web版本

    图像问答系统的设计与实现,可以作为项目来学习,主要功能调用了百度AI开放平台的服务实现。 缺点:(1).没有调用框架,只使用了MVC开发模式。 (2).部分功能兼容IE浏览器,不兼容chrome浏览器。

    spring面试问答.pdf

    spring和springMVC的一些面试题问答,包含Spring 事务底层原理 、启动、运行流程、设计模式等

    基于深度学习的视觉问答系统源码+文档说明+答辩ppt(高分毕业设计).zip

    基于深度学习的视觉问答系统源码+文档说明+答辩ppt(高分毕业设计).zip的主要工作在于本文使用open-ended模式,答案的准确率采用分数累积,而不是一般的多项选择。本文采用CSF模块(包括CSF_A和CSF_B)不仅对...

    基于Bert知识三元组的简单的问答系统python源码+项目说明(joint+pipeline模式).zip

    本项目是一个基于知识三元组的简单的问答系统,分为joint和pipeline两种模式。joint模式为基于bert做意图识别和命名体识别的联合学习训练得到的模型,pipeline模式为单独训练命名实体识别和QA相似度模型。 ...

    考研软件工程复试笔试面试详细问答.docx

    内容概要:本文为华科...阅读建议:此资源以一问一答的方式学习软件工程方向的原理和内核容,不仅是代码编写实现也更注重内容上的需求分析和方案设计,所以在学习的过程要结合本科四年学习的各种课程内容一起来实践。

    医学知识图谱构建与基于模式匹配的问答技术研究 (后端部分).zip

    同时,知识图谱还能支撑高级的人工智能应用,比如问答系统、推荐系统、决策支持等领域。 构建知识图谱的过程通常包括数据抽取、知识融合、实体识别、关系抽取等多个步骤,涉及到自然语言处理、机器学习、数据库技术...

    Java面试问答,免费下载

    java集合、基础、jvm、spingmybatis、mysql、计网、线程、设计模式、nginx、Kafka、Linux、RabbitMQ等等面试问答(包含答案),免费下载

Global site tag (gtag.js) - Google Analytics