61阅读

交互式系统原理与设计-如何利用交互设计的接近法则与格式塔理论

发布时间:2017-12-21 所属栏目:系统分析报告的作用

一 : 如何利用交互设计的接近法则与格式塔理论

本文来源中国雅虎UED 早在20世纪早期,一个由德国心理学家组成的研究小组试图解释人类视觉的工作原理。他们观察了许多重要的视觉现象并编订了目录。格式塔理论明确地提出:眼脑作用是一个不断组织、简化、统一的过程,正是通过这一过程,才产生出易于理解、协调的整体。

我们的视觉系统自动对视觉输入构建结构,并且在神经系统层面上感知形状,图形和物体,而不是只看到互不相连的边,线和区域。“形状”和“图形”在德语是“Gestalt”,因此这些理论也被称为视觉感知的格式塔(Gestalt)原理。

接近法则是格式塔理论中最为我们所熟知的,也是我们最常用到的一项法则。说的是物体之间的相对距离会影响我们感知它们是否以及如何在一起。相对于其它物体来说,彼此之间靠近的物体看起来属于一组。越接近,组合在一起的可能性就越大,强调的是位置。如下图,我们在意识中会将其分为两组。

 如何利用交互设计的接近法则与格式塔理论

接近原则被广泛应用于页面内容的组织,以及分组设计中。对于引导用户的视觉流及方便用户对界面的解读起到非常重要的作用,通过接近原则对同类内容进行分组,同时留下间距,会给用户的视觉以秩序和合理的休憩。 设计者们经常使用分组框或分割线将屏幕上的控件和数据显示分隔开,如下图。

 如何利用交互设计的接近法则与格式塔理论 如何利用交互设计的接近法则与格式塔理论

除了接近法则之外,还有很多重要的原则你知道吗?接下来我将逐一向大家介绍。

相似原则:

相似,听起来跟接近非常类似,但是他们确实是不同的两个概念。接近强调位置,而相似则强调内容。人们通常把那些明显具有共同特性(如形状、大小、颜色等)的事物组合在一起,即相似的部分在知觉中会形成若干组。在下图中,我们会将黑帽子蛋和红帽子蛋自然的分成两组。

 如何利用交互设计的接近法则与格式塔理论

在页面设计中分类使用文本、颜色、图像等,可以更好的区分各个模块和内容。如下图中雅虎读书导航,通过相似原则我们很容易的将一级导航和二级导航区分开来。

 如何利用交互设计的接近法则与格式塔理论

换一个角度来思考,颜色的变化很清楚的区分了不同的内容,相似中的逆向思维是获取焦点的好方法。这种方法在导航和强调部分信息的设计上有着广泛地应用,如下图。

 如何利用交互设计的接近法则与格式塔理论

封闭原则:

当元素不完整或者不存在的时候,依然可以被人们所识别。根据过去以往的经验和视知觉的整体意愿驱使,我们习惯性的将图形作为一个整体去观看,于是我们在脑袋里将缺少的形状补充进去之后,便形成最终我们识别出来的图形效果。如下图,没有三角形和圆,但是在我们心理模型中填充缺失的信息,创建我们熟知的形状和图形。

 如何利用交互设计的接近法则与格式塔理论

封闭原则常被应用于图形界面和页面设计中,例如:在页面中经常会用堆叠的形式表示对象的集合,只要显示一个完整的对象和其“背后”对象的一角就足以让用户感知到这是由一叠对象构成的整体,如下图所示。

连续原则:

与封闭性相关的是连续原则,连续原则说的是我们的视觉倾向于感知连续的形式而不是零散的碎片。在图形设计中,使用了连续原则的一个最广为人知的例子就是IBM的标志。它由非连续性的蓝色块组成,但一点也不含糊,我们很容易就能识别出三个粗体字母,就像透过百叶窗看到的效果一样,见图9。

关于格式塔的原则还有很多,而且可以复合使用。在我们设计了一个东西之后,可以试着使用格式塔原理来考量一下自己各个设计元素之间的关系是否复合设计的初衷。希望通过对格式塔原始理论的介绍可以给大家带来一些启发

二 : 分布式系统设计原理与方案

一直在思考分布式系统设计的问题,业务对象原封不动的情况下部署在客户端和服务器端,可以根据配置文件选择是连接服务器还是连接本地的数据库,这个问题让我绞尽脑汁,我总是设想的客户端与服务器端通信的方式是最低端的Socket。(www.61k.com)花了两个晚上研究CSLA.NET框架关于数据门户这块代码,才发现问题的关键所在:客户端与服务器端通信不能采用最低端的Socket,而要用高端的WebService、.NET Remoting或者是自己定义一种协议等,只要它们支持客户端直接根据服务器端的服务URL、类名、方法名和方法参数四个信息就可以调用服务器对应的类和方法就行。

说明:本文中所表达的思想与CSLA.NET有很大区别,不要看了本文就以为是CSLA.NET的设计思想,也不要以为本文错误的解释了CSLA.NET,这不是一篇介绍CSLA.NET的文章,但纯思想上它们是相同的。

  • 分布式系统的部署

平常我们都说三层架构,我认为它是一个广义的模型,更多层的设计可以合并相邻几层的方式最终回归到三层这个宽泛的概念上来,我的意思是:这些都只是概念,忘记这些概念去实际分析设计会离这些概念更近一些。

接下来我要把三层变的更简单点,两层,数据访问层合并到业务层,统称为业务层,因为我们面对的问题不是分层的问题,而是分布式系统中各层应该怎么部署的问题。在CSLA.NET书中也说到业务层和数据访问层放到同一台机器上可以提高性能和容错性。因此他们俩的合并不影响分布式系统的部署。

不过要解释的是数据库系统(CSLA.NET中说的数据存储和管理层)并没有考虑到三层中来,也就是它不包含在数据访问层中,如果把它算进来,那么它是在数据访问层之下单独存在的。

综上,在分布式系统部署角度考虑的分层实际是三层:界面层、业务层(包含数据访问层的业务层)、数据存储层。

下面举例说明可能的部署情景,带阴影的框框表示一台机器,虚线框表示根据使用场合可有可无,虚横线表示从此处划开单独出服务器。在B/S应用中,Web浏览器为客户端,其他全部为服务器。在C/S应用中,处在最上层的界面层+业务层为客户端,其他为服务器

非分布式系统的部署

分布式系统 分布式系统设计原理与方案

单机版

分布式系统 分布式系统设计原理与方案

两三台机器

分布式系统的部署

分布式系统 分布式系统设计原理与方案

分布式的Web系统

分布式系统 分布式系统设计原理与方案

分布式的C/S系统

有几点要说明:
1. 客户端上的验证等业务逻辑是不可信的,因此任何一种部署都需要服务器端包含业务层;
2. 为了开发、维护和部署中的高度可伸缩性,图中的各业务层所包含的代码都是一模一样的;
3. 因为第2点,所以我遇到了业务层的同一个操作是与其他机器上的业务层通信还是访问数据库这个难题。

解决业务层的数据访问问题

这个问题是关键问题,也就是上面几点说明中的第3个问题,为了解决这个问题我们引入数据门户的概念。

下面以WebService为例说明:界面层访问本机的业务对象的增删改查中的“查”方法时,跳过数据库的查询操作,访问另一台机器中的同一个业务对象类的“查”方法。

分布式系统 分布式系统设计原理与方案

以上是向另一台机器发送请求,该请求并不直接调用另一台机器上的业务对象类的“查”方法,而是将要调用的业务对象和方法参数信息转为一个“二进制包”,作为参数去调用另一台机器上通用的“查”方法,另一台机器上的“查”方法再解开这个包,然后去调用解开的包中所表示的业务对象类型,下面的静态图是另一台机器接受到请求后的工作。

分布式系统 分布式系统设计原理与方案

又有些说明:
1. 关于原理都已在图中做了描述,不另写大段文字解释了;
2. 上面两个图中,除了“实际业务对象类”以外的部分全部属于架构或者框架部分;
3. 如果用OO的思想去审查上面的两个图,你一定会为这糟糕的设计而抱怨,这里只是为了尽可能简单的表述分布式系统的工作原理,你可以采用策略模式使数据门户不改变的情况下适应各种请求响应场合,采用工厂模式实现不同的请求响应场合的切换。

关于数据库的分布

为了解决数据库服务器的负担,我们可能希望把数据分布存储在多个服务器上,我设想的数据库分布方案是,各服务器上的数据库在结构上一模一样,而表里的数据存储到不同服务器上,这样数据访问层在查数据的时候分别向所有数据库服务器发送同样的sql命令,然后数据访问层得到数据后整合,这样减轻每台服务器的工作量。亦或者根据表里的某个代表性的字段(如:省份)分布数据到不同服务器。

三 : 最新畅言交互式教学系统考试考核试题58

畅言交互式多媒体教学系统

应用操作考核试题

一、 必考题:(20分)

登录畅言名师100教育资源网,将计算机桌面上“马中资源”文件内的任一个教学资源上传到自己的网盘里。

二、 抽考题:畅言-电子课本操作试题(采取抽签方式确定考核一题40分) 第一题:

1.打开电子课本的某一页,呈单页面; 2.用画笔在课文的某一段内容用红色划线; 3.在旁边用荧光笔写上“此处为重点”五个字; 4.擦掉“此处为”三个字; 5.把此页面放大,后面的学生能看清为宜。

第二题:

1.打开电子课本,呈双页面; 2.用聚焦功能画出课文中某一块区域; 3.改变聚焦区域的大小并在页面上拖动; 4.拖到某个位置后将方形变为椭圆形状;

5.打开灯光再关闭灯光; 6.将聚焦的区域以卡片的形式生成到电子白板页面上. 第三题:

1.打开电子课本,呈双页面; 2.用聚焦功能画出课文中某一块区域; 3.将聚焦区域进行全屏播放; 4.用蓝色粗笔在上面划线和批注; 5.返回到课本页面并关闭。

第四题:

1.打开电子课本,呈双页面,从桌面马中资源中导入一张照片,嵌入到课本的右上角; 2.播放该照片再关闭。 3.返回到电子课本。

第五题:

1.打开电子课本的某一课呈双页面; 2.进入备课状态,点击资源库并登录;

3.将桌面上马中资源中的一首歌上传到“我的空间”; 4.把该首歌曲嵌入到课本当前页的第二页的右下方; 5.播放该歌曲再关闭。

第六题:

1.打开电子课本呈双页面,进入备课状态; 2.点击资源库中的资源云平台,任选一个文件嵌入到课本页面第二页的中间位置; 3.先播放再关闭。 第七题:

1.打开电子课本呈双页面,进入备课状态; 2.点击进入资源库; 3.根据自己所任教学科从“诗词对答”或“虚拟实验”或“演示样例”或“u盘资源”中,任选一个文件嵌入到课本页面第二页的中间位置; 3.先播放再关闭。 第八题:

1.打开电子课本呈双页面,进入备课状态; 2.点击进入资源库; 3.根据自己所任教学科从“发音评测卡片”或“图片画廊卡片”或“随堂测试卡片”中,

任选一个模板制作一张卡片文件;4.将制作的卡片嵌入到课本页面第二页的中间位置; 4.先播放再关闭。

第九题:

1.打开电子课本呈双页面,进入备课状态; 2.利用智能搜索,输入汉字“狗”;

3.将搜索的相关资源任选一个嵌入到课本页面第二页的中间位置; 4.先播放再关闭。

第十题:

1. 打开电子课本呈双页面,进入备课状态; 2.打开桌面“马中资源”内的一张图片嵌入到课本第一页面的中间位置; 3.先播放再关闭; 4.把当前电子课本的各种资源形成资源包导出并储存在“考核教师自作课件”文件夹内; 5.从计算机中找出这个授课文件并导入到当前课本内。

三、抽考题:畅言-电子白板操作试题(采取抽签方式确定考核一题40分) 第一题:

1.打开电子白板,新建一个空白页面; 2.打开桌面上马中资料内“狗”的图片,克隆或复制成4只; 3.将其中的一只放大变成大狗,成为一只大狗带领一群小的画面; 4.在图片下面板书:4+1=5,板书颜色为红色; 5.将第四步“撤消、重做”;

6.最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面。

第二题:

1.打开白板软件,新建一个空白页面,在页面中间标上“1”; 2.在新建一个页面,在页面中间用黑色笔标上“2”; 3.用翻页功能回到第一页; 4.在第一页上画出一个直角三角形; 5.在第二页插入资源库中的任意一幅图片; 6.最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面。

第三题:

1.打开白板软件; 2.打开桌面上一个“课堂教学模式”文档; 3.用钢笔依次四种颜色标出四个重点环节; 4.用黄色毛笔圈注“课堂教学模式”四个字; 5.将“课堂教学模式””四个字放大; 6.最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面。

第四题:

1.打开白板软件,切换到电脑桌面操作模式; 2.回到白板模式,新建一个页面; 3.板书“马店中心校”5个字; 4.旋转这5个字; 5.擦除前面的“马店”两字; 6.撤销最后一步的操作; 7.最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 8.将你在白板上的操作全部删除

返回到书架页面。

第五题:

1.打开白板软件; 2.打开桌面上的“马中资源”内《4.2八年级情系祖国(粤教版)》PPT课件,在白板模式下进行演示一遍; 3.用翻页功能打开第三张PPT并对其中重要部分用紫色毛笔进行标注; 4.再分别打开第二张和第四张PPT并分别对其中重要部分合适的颜色毛笔进行标注; 5.结束PPT播放,保证批注不能嵌入PPT; 6.最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面。

第六题:

1.打开白板软件; 2.打开桌面上的国旗图片,用紫色毛笔板书“国旗”两个字; 3.将两个字的颜色改为蓝色; 4.然后旋转这两个字; 5.最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 6.将你在白板上的操作全部删除返回到书架页面。

第七题:

1.打开白板软件; 2. 根据自己所任教学科从图形工具栏内选择一个图形或工具并拖动到电子白板上; 3.将该图形或工具图克隆成四个; 4.将其中的一个顺时针旋转,一个逆时针旋转,一个填上红色,一个填上蓝色; 5.将这四个图形上对齐并合并; 5. 最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 6.将你在白板上的操作全部删除返回到书架页面。

第八题:

1.打开白板软件; 2.点击进入资源库; 3.根据自己所任教学科从“资源云平台”或“诗词对答”或“虚拟实验”或“演示样例”或“音乐专区”或“u盘资源”中,任选一个文件拖动到电子白板上; 3.先播放再关闭; 5. 最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名;

6.将你在白板上的操作全部删除返回到书架页面。

第九题:

1.打开白板软件; 2.利用智能划词搜索功能搜索“梅花”等相关资源; 3.从中任选一个文件资源拖动到电子白板上; 4.先播放再关闭; 5. 最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名;

6.将你在白板上的操作全部删除返回到书架页面。

第十题:

1.打开白板软件; 2.利用打开工具栏将“马中资源”内的一个授课文件导入在电子白板上; 3.先播放其中的一个资源再关闭; 4.新建一个页面并从“马中资源”内导入一篇考试卷; 5.用红笔在试卷上批注 6. 最后将该页面保存到桌

面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面。

第十一题:

1.打开白板软件; 2.在第一页上写一个成语; 3.新建一个页面并从“马中资源”内导入一张照片; 4. 在该张照片上剪辑一个人的图像并生成在新的页面上 5.用蓝笔在剪辑的照片上写上“朋友”两字的批注 6. 最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面。

第十二题:

1.打开白板软件; 2.在第一页上写一个成语; 3.新建一个页面并从“马中资源”内导入一张图片; 4. 给该页面选择合适的背景; 5.用幕布完全遮住该画面 6.把幕布从右向左徐徐拉开; 7. 最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面

第十三题:

1.打开白板界面; 2.在第一页上写“用思想引领文化”并进行语音朗读等智能演示; 3.新建页面板书词语“学习、快乐、教书、育人”并对其进行智能演示;

4. 汉语拼音; 5.用幕布完全遮住该画面 6.把幕布从右向左徐徐拉开; 7. 最后将该页面保存到桌面“考核教师自作课件”文件夹内,并用自己的姓名作为课件的文件名; 7.将你在白板上的操作全部删除返回到书架页面

四、成立考核组:

组长:李越 副组长:张国丰 组员:丁求勇 刘泽利 龚玉华

五、参加考核安排:

第一批:张敏(小) 董璇 杨先燕 李大娟 范林 张红林 李妮 周名云

曹见坤

第二批:王应峰 曹天明 朱习福 张敏(大) 曹全友 季全宝 于杨

王凤山 刘泽利 范后彬

第三批:李文芳 张民军 李红星 龚玉华 张成新 李忠厚 杨杰 杭永玲

李新堂 冯庆霞 丁求勇

四 : 应用系统中交互式报表功能解析

从报表需求的整个发展历程来看,可以分为两个阶段:

1、静态报表:解决显示、打印、导出报表数据的需要。[www.61k.com)

2、交互式报表:解决终端用户分析数据的需要,通常会用到数据可视化、向下钻取、贯穿钻取、数据过滤、数据排序等功能。

这篇文章主要介绍ActiveReports中交互式报表中常用到的数据分析方法。

(一) 数据可视化

数据可视化技术是将数据以图形化的方式进行显示,让数据更易于阅读、理解和分析。早期的数据可视化以图表(Chart)为主,现代商业报表中逐渐加入迷离图(Sparkline)、数据条(Bullet)、图标集(Icon)、仪表盘(Gauge)、地图(Map)用于数据可视化,而数据可视化常用作交互式报表的基本载体。实现步骤。

报表数据分析 应用系统中交互式报表功能解析

(二) 向下钻取

向下钻取是根据终端用户的需求,动态的显示或隐藏报表数据,常用于汇总类型报表。向下钻取报表在设计模板时,所有的数据显示均在同一个报表内完成。在运行时,可以通过终端用户点击的操作来折叠或展开明细数据。实现步骤。

报表数据分析 应用系统中交互式报表功能解析

(三) 贯穿钻取

贯穿钻取是通过点击一级报表区域,跳转至二级报表的一种报表数据分析方式。报表设计时需要完成一级、二级两个报表的模板,并通过参数建立两级报表之间的关系。常见的一级报表可以使用交互式图表来实现,通过点击图表区域可以跳转至二级报表。实现步骤。

报表数据分析 应用系统中交互式报表功能解析

(四) 数据过滤

动态过滤是在运行时为用户提供数据过滤的功能,用户可以根据自己的需要选择关系的数据进行查看。通过这种方式可更准确的提供用户关系的报表数据。常用的过滤分为单条件过滤、组合条件过滤、级联条件过滤。实现步骤。

报表数据分析 应用系统中交互式报表功能解析

(五) 数据排序

动态排序是为最终用户提供对报表数据排序的能力。虽然该功能在应用系统中经常用到,比如用表格显示数据时可以很方便的完成数据排序操作,但是,早期的静态报表中生成的报表不具备用户交互能力,所以,这也是现代商业报表中具有的一个功能。实现步骤。

报表数据分析 应用系统中交互式报表功能解析

五 : 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

鸭子-策略模式(Strategy)

前言

万事开头难,最近对这句话体会深刻!这篇文章是这个系列正式开始介绍设计模式的第一篇,所以肩负着确定这个系列风格的历史重任,它在我脑袋里默默地酝酿了好多天,却只搜刮出了一点儿不太清晰的轮廓,可是时间不等人,以后再多“迭代”几次吧!在前面的随笔里,我已经提到了,这个系列准备以《Head First Design Patterns》的结构为主线,所以每个模式的核心故事都是取材于此书,在此再次声明一下。[www.61k.com)不管怎样,宗旨是为了跟大家一起循序渐进地去认识设计模式。

上一篇:模式和原则,得到很多朋友的支持和鼓励,这里再次深表感谢。这里我还是想呼吁一下,希望大家看过后多提宝贵意见,反对意见更好,关键是我们在互动中可以共同进步,因为经验告诉我讨论(争论更甚)出来的火花,总是印象最深刻的。

其实策略模式是一个很简单的模式,也是一个很常用的模式,可谓短小精悍。我在介绍这个模式的同时,为了加深大家对OO的理解,还会反复强调前面讲过的设计原则和GRASP模式。这个系列的文章前后多少会有一些关联的连续性,但是单独一篇文章针对单一模式也一定是独立的,所以不论大家想从前往后连续看也好,还是挑喜欢的跳着看,都没有问题。

“罗嗦了这么多,太唐僧了吧,快点开始吧…”(烂西红柿和臭鸡蛋从四面八方飞来)

模拟鸭子

Joe是一名OO程序员,他为一家开发模拟鸭子池塘游戏的公司工作,该公司的主要产品是一种可以模拟展示多种会游泳和呷呷叫的鸭子的游戏。这个游戏是使用标准的面向对象技术开发的,系统里所有鸭子都继承于Duck基类,系统的核心类图如下:

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

如图所示,在Duck基类里实现了公共的quack()和swim()方法,而MallardDuck和RedheadDuck可以分别覆盖实现自己的display()方法,这样即重用了公共的部分,又支持不同子类的个性化扩展。从目前的情况看,这是一个很好的设计,哈!  

但是,商场如战场,不进则退。Joe的公司最近的日子不好过,盗版泛滥,再加上竞争对手的围追堵劫,已经拖欠好几个月工资了。因此,公司高层在一次集体“腐败”后,决定一定要给系统增加一些超玄的功能,以彻底击垮竞争对手。经过董事会讨论,最终觉得如果能让鸭子飞起来,那么一定可以给对手致命一击。于是Joe的上司对董事们拍着胸脯说:“这没有问题,Joe是一个OO程序员,这对他来说太简单了!我们保证一周内结束战斗。”

接到任务的Joe丝毫不敢怠慢,研究了上级的指示以后,发现只要在Duck里增加一个fly()方法就可以搞定了,这样所有继承Duck的鸭子就都拥有了会飞的能力,哈!这回奖金有盼头啦!改进后的系统类图如下:

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

   Joe的上司很高兴,带着新产品给董事们演示去了……  

……

Joe的上司:“我正在给董事们演示你会飞的鸭子,但是怎么有很多橡皮鸭子也在四处乱飞呢?你在耍我吗?你还想不想混啦?!”(此处省略粗话100字)

Joe被吓坏了,到手的奖金泡汤了!冷静下来的Joe发现,原来在Duck类里增加的方法,也同样被继承于Duck的RubberDuck类继承了,所以就有了会飞的橡皮鸭子,这是严重违反该系统“真实模拟各种鸭子”的原则的!那么该怎么办呢?Joe很郁闷!他突然想到:如果在RubberDuck类里把fly()方法重写一下会如何?在RubberDuck类的fly()里让橡皮鸭子什么都不做,不就一切OK了吗!那以后再增加一个木头鸭子呢?它不会飞也不会叫,那不是要再重写quack()和fly()方法,以后再增加其它特殊的鸭子都要这样,这不是太麻烦了,而且也很混乱。

最终,Joe认识到使用继承不是办法,因为他的上司通知他,董事会决定以后每6个月就会升级一次系统,以应对市场竞争,所以未来的变化会很频繁,而且还不可预知。如果以后靠逐个类去判断是否重写了quack()或fly()方法来应对变化,显然混不下去!

(Joe这时很迷惑,为什么屡试不爽的继承,在系统维护升级的时候,无法很好地支持重用呢?)

那么使用接口怎么样?我可以把fly()方法放在接口里,只有那些会飞的鸭子才需要实现这个接口,最好把quack()方法也拿出来放到一个接口里,因为有些鸭子是不会叫的。就像下面这样:

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

Joe的上司知道后怒了:“你这样做难道是希望所有需要quack()和fly()方法的鸭子都去重复实现这两个方法的功能吗?就这么几个鸭子还好说,但是我们有几十、上百个鸭子的时候你怎么办?如果某个方法要做一点修改,难道你要重复修改上百遍吗?你是不是疯啦?”

呵呵!如果你是Joe,你该怎么办?

我们知道,并不是所有的鸭子都会飞、会叫,所以继承不是正确的方法。但是虽然上面的使用Flyable接口的方法,可以解决部分问题(不再有会飞的橡皮鸭子),但是这个解决方案却彻底破坏了重用,它带来了另一个维护的噩梦!而且还有一个问题我们前面没有提到,难道所有的鸭子的飞行方式、叫声等行为都是一模一样的吗?不可能吧!

说到这里,为了能帮助Joe摆脱困境,我们有必要先停下来,重新回顾一些面向对象设计原则。请您告诉我:“什么东西是在软件开发过程中是恒定不变的?”,您想到了吗?对,那就是变化本身,正所谓“计划没有变化快”,所以直面“变化这个事实”才是正道!Joe面对的问题是,鸭子的行为在子类里持续不断地改变,所以让所有的子类都拥有基类的行为是不适当的,而使用上面的接口的方式,又破坏了代码重用。现在就需要用到我们的第一个设计原则:

Identify the aspects of your application that vary and separate them from what stays the same.(找到系统中变化的部分,将变化的部分同其它稳定的部分隔开。)

换句话说就是:“找到变化并且把它封装起来,稍后你就可以在不影响其它部分的情况下修改或扩展被封装的变化部分。” 尽管这个概念很简单,但是它几乎是所有设计模式的基础,所有模式都提供了使系统里变化的部分独立于其它部分的方法。

OK!现在我们已经有了一条设计原则,那么Joe的问题怎么办呢?就鸭子的问题来说,变化的部分就是子类里的行为。所以我们要把这部分行为封装起来,省得它们老惹麻烦!从目前的情况看,就是fly()和quack()行为总是不老实,而swim()行为是很稳定的,这个行为是可以使用继承来实现代码重用的,所以,我们需要做的就是把fly()和quack()行为从Duck基类里隔离出来。我们需要创建两组不同的行为,一组表示fly()行为,一组表示quack()行为。为什么是两组而不是两个呢?因为对于不同的子类来说,fly()和quack()的表现形式都是不一样的,有的鸭子嘎嘎叫,有的却呷呷叫。有了这两组行为,我们就可以组合出不同的鸭子,例如:我们可能想要实例化一个新的MallardDuck(野鸭)实例,并且给它初始化一个特殊类型的飞行行为(野鸭飞行能力比较强)。那么,如果我们可以这样,更进一步,为什么我们不可以动态地改变一个鸭子的行为呢?换句话说,我们将在Duck类里包含行为设置方法,所以我们可以说在运行时改变MallardDuck的飞行行为,这听起来更酷更灵活了!那么我们到底要怎么做呢?回答这个问题,先要看一下我们的第二个设计原则:

Program to an interface, not an implementation.(面向接口编程,而不要面向实现编程。)

嘿!对于这个原则,不论是耳朵还是眼睛,是不是都太熟悉了!“接口”这个词已经被赋予太多的含义,搞的大家一说点儿屁事就满嘴往外蹦“接口”。那么它到底是什么意思呢?我们这里说的接口是一个抽象的概念,不局限于语言层面的接口(例如C#里的interface)。一个接口也可以是一个抽象类,或者一个基类也可以看作是一种接口的表现形式,因为基类变量可以用来引用其子类。要点在于,我们在面向接口编程的时候,可以使用多态,那么实际运行的代码只依赖于具体的接口(interface,抽象类,基类),而不管这些接口提供的功能是如何实现的,也就是说,接口将系统的不同部分隔离开来,同时又将它们连接在一起。我的神啊!接口真是太伟大了!(烂西红柿和臭鸡蛋从四面八方飞来)

OK!这回该彻底解决Joe的问题了!

根据面向接口编程的设计原则,我们应该用接口来隔离鸭子问题中变化的部分,也就是鸭子的不稳定的行为(fly()、quack())。我们要用一个FlyBehavior接口表示鸭子的飞行行为,这个接口可以有多种不同的实现方式,可以“横”着分,也可以“竖”着分,管它呢!这样做的好处就是我们将鸭子的行为实现在一组独立的类里,具体的鸭子是通过FlyBehavior这个接口来调用这个行为的,因为Duck只依赖FlyBehavior接口,所以不需要管FlyBehavior是如何被实现的。如下面的类图,FlyBehavior和QuackBehavior接口都有不同的实现方式!

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

Joe已经晕了,“你说了这么多,全是大白话,来点代码行不行,我要C#的!”。说到这里,我们也该开始彻底改造这个设计了,并会在最后附加部分代码来帮助大家理解。  

第一步:我们要给Duck类增加两个接口类型的实例变量,分别是flyBehavior和quackBehavior,它们其实就是新的设计里的“飞行”和“叫唤”行为。每个鸭子对象都将会使用各种方式来设置这些变量,以引用它们期望的运行时的特殊行为类型(使用横着飞,吱吱叫,等等)。

第二步:我们还要把fly()和quack()方法从Duck类里移除,因为我们已经把这些行为移到FlyBehavior和QuackBehavior接口里了。我们将使用两个相似的PerformFly()和PerformQuack()方法来替换fly()和qucak()方法,后面你会看到这两个新方法是如何起作用的。

第三步:我们要考虑什么时候初始化flyBehavior和quackBehavior变量。最简单的办法就是在Duck类初始化的时候同时初始化他们。但是我们这里还有更好的办法,就是提供两个可以动态设置变量值的方法SetFlyBehavior()和SetQuackBehavior(),那么就可以在运行时动态改变鸭子的行为了。

下面是修改后的Duck类图:

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

我们再看看整个设计修改后的类图:

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

最后大家再看看演示代码,因为代码比较多,就不贴出来了,大家可以下载后参考:策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]。下面是演示代码的执行结果:

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

这就是策略模式

前面说了那么多,现在终于到了正式介绍我们今天的主角的时候啦!此刻心情真是好激动啊!其实我们在前面就是使用Strategy模式帮Joe度过了难过,真不知道他发了奖金后要怎么感谢我们啊。OK!下面先看看官方的定义:

The Strategy Pattern definesa family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.(策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。)

怎么样,有了前面Joe的经历,这个定义理解起来还不那么太费劲吧?我想凡是认真看到这里的人,应该都能理解的。那么下面再画蛇添足地罗嗦几句,给那些还不太理解的朋友一个机会吧。J

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

Context(应用场景):

l        需要使用ConcreteStrategy提供的算法。

l        内部维护一个Strategy的实例。

l        负责动态设置运行时Strategy具体的实现算法。

l        负责跟Strategy之间的交互和数据传递。

Strategy(抽象策略类):

l        定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。

ConcreteStrategy(具体策略类):

l        实现了Strategy定义的接口,提供具体的算法实现。

还不理解?!我的神啊!那再看看下面的顺序图吧,这是最后的机会啦!

策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]

应用场景和优缺点

上面我们已经看过了Strategy模式的详细介绍,下面我们再来简单说说这个模式的优缺点吧!怎么说呢,人无完人,设计模式也不是万能的,每一个模式都有它的使命,也就是说只有在特定的场景下才能发挥其功效。我们要使用好模式,就必须熟知各个模式的应用场景。

对于Strategy模式来说,主要有这些应用场景:

1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。(例如FlyBehavior和QuackBehavior)

2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。(例如FlyBehavior和QuackBehavior的具体实现可任意变化或扩充)

3、 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。

对于Strategy模式来说,主要有如下优点:

1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。

2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。

3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

对于Strategy模式来说,主要有如下缺点:

1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

备注:关于场景和优缺点,上面肯定说得不够全面,欢迎大家来补充。

.NET框架里的应用

Strategy模式的应用非常广泛,也许大家有意无意之间一直都在使用。这里举一个.NET框架里使用Strategy模式的例子,象这样的例子其实还有很多,只要大家细心体会就一定会发现的。

如果写过程序,那么ArrayList类肯定都会用过吧,那么它的Sort方法想必大家也一定不陌生了。Sort方法的定义如下:

public virtualvoidSort (IComparercomparer)

可以看到Sort方法接收一个IComparer类型的参数,那么这个IComparer接口是做什么用的呢?下面我们看一段程序,下面的代码示例演示如何使用默认比较器和一个反转排序顺序的自定义比较器,对ArrayList中的值进行排序。(完全引自MSDN:ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref2/html/M_System_Collections_ArrayList_Sort_1_a2d90598.htm)

 1策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]using System;
 2策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]using System.Collections;
 3策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
 4策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]public class SamplesArrayList  策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]{
 5策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原] 
 6策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]   public class myReverserClass : IComparer  策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]{
 7策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
 8策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      // Calls CaseInsensitiveComparer.Compare with the parameters reversed.
 9策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      int IComparer.Compare( Object x, Object y )  策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]{
10策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]          return( (new CaseInsensitiveComparer()).Compare( y, x ) );
11策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      }
12策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
13策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]   }
14策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
15策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]   public static void Main()  策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]{
16策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原] 
17策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      // Creates and initializes a new ArrayList.
18策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      ArrayList myAL = new ArrayList();
19策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "The" );
20策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "quick" );
21策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "brown" );
22策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "fox" );
23策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "jumps" );
24策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "over" );
25策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "the" );
26策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "lazy" );
27策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Add( "dog" );
28策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原] 
29策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      // Displays the values of the ArrayList.
30策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      Console.WriteLine( "The ArrayList initially contains the following values:" );
31策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      PrintIndexAndValues( myAL );
32策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原] 
33策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      // Sorts the values of the ArrayList using the default comparer.
34策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Sort();
35策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      Console.WriteLine( "After sorting with the default comparer:" );
36策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      PrintIndexAndValues( myAL );
37策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
38策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      // Sorts the values of the ArrayList using the reverse case-insensitive comparer.
39策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      IComparer myComparer = new myReverserClass();
40策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      myAL.Sort( myComparer );
41策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      Console.WriteLine( "After sorting with the reverse case-insensitive comparer:" );
42策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      PrintIndexAndValues( myAL );
43策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
44策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]   }
45策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原] 
46策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]   public static void PrintIndexAndValues( IEnumerable myList )  策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]{
47策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      int i = 0;
48策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      foreach ( Object obj in myList )
49策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]         Console.WriteLine( "\t[{0}]:\t{1}", i++, obj );
50策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]      Console.WriteLine();
51策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]   }
52策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
53策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]}
54策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
55策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
56策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]/**//* 
57策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]This code produces the following output.
58策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]The ArrayList initially contains the following values:
59策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [0]:    The
60策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [1]:    quick
61策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [2]:    brown
62策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [3]:    fox
63策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [4]:    jumps
64策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [5]:    over
65策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [6]:    the
66策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [7]:    lazy
67策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [8]:    dog
68策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
69策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]After sorting with the default comparer:
70策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [0]:    brown
71策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [1]:    dog
72策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [2]:    fox
73策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [3]:    jumps
74策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [4]:    lazy
75策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [5]:    over
76策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [6]:    quick
77策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [7]:    the
78策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [8]:    The
79策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]
80策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]After sorting with the reverse case-insensitive comparer:
81策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [0]:    the
82策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [1]:    The
83策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [2]:    quick
84策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [3]:    over
85策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [4]:    lazy
86策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [5]:    jumps
87策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [6]:    fox
88策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [7]:    dog
89策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]        [8]:    brown 
90策略模式 设计模式随笔系列:鸭子-策略模式(Strategy)[原]*/

怎么样,大家看出来了吧,其实在这段代码里,ArrayList相当于Strategy模式中的Context(应用场景)部分,而IComparer相当于Strategy(抽象策略类)部分,myReverserClass相当于ConcreteStrategy(具体策略类)部分。我们这里抛开myReverserClass类的Compare方法如何具体实现不谈,我们只要知道这是一个具体策略类,它提供了应用场景需要的具体算法,它实现了抽象策略类接口,而应用场景通过抽象策略类动态调用到了具体策略类中的算法。哈!所以这是一个十分典型的Strategy模式的应用。

基于这个符合Strategy模式的结构,我们还可以提供很多种自定义的具体策略类的实现,只要这些类实现了IComparer接口,就可以在运行时动态设置给ArrayList类的Sort方法,在Sort方法中会根据具体策略类实现的比较算法规则来对ArrayList中的数据进行排序。

最后一个设计原则

关于Strategy模式的故事讲到这里,应该基本OK啦!下面我们再聊些更高层次的东西。什么是更高层次的东西?嘿!当然是设计原则了!在前面总结Strategy模式的优点的时候我们提到过,Strategy模式不仅保留了继承的优点,而且还提供了更灵活的扩展能力。为什么会这样呢?Strategy模式是怎么做到这一点的呢?哈!这是因为它“上面有人”啊!谁啊?它就是我们下面要介绍的重量级设计原则:

Favor composition over inheritance.(优先使用对象组合,而非类继承)

关于组合和继承,我们只要这样来理解即可:组合是一种“HAS-A”关系,而继承是一种“IS-A”关系。很明显“HAS-A”要比“IS-A”更灵活一些。也就是说在创建系统的时候,我们应该优先使用对象组合,因为它不仅可以给你提供更多灵活性和扩展性,而且还使你可以在运行时改变行为(组合不同的对象),这简直是酷毙了!但是也不是说继承就是不能用,只是说应该把继承应用在相对更稳定,几乎没有变化的地方,例如前面的Duck类里的Swim()方法,因为可以肯定所有鸭子一定都会游泳,所以就没有必要给这个行为提供基于Strategy模式的实现方式,因为那样做除了是程序更复杂以外,没有什么意义。

BULLET POINTS

l       Knowing the OO basics does not make you a good OO designer.

l       Good OO designs are reusable,extensible and maintainable.

l       Patterns show you how to build systems with good OO design qualities.

l       Patterns are proven object oriented experience.

l       Patterns don’t give you code,they give you general solutions to design problems.You apply them to your specific application.

l       Patterns aren’t invented,they are discovered.

l       Most patterns and principles address issues of change in software.

l       Most patterns allow some part of a system to vary independently of all other parts.

l       We often try to take what varies in a system and encapsulate it.

l       Patterns provide a shared language that can maximize the value of your communication with other developers.

作者

王晓亮/Justin

MSN:xiaoliang203@hotmail.com

Mail:xiaoliang.justin@gmail.com

参考资料

《UML和模式应用》

《敏捷软件开发—原则、模式与实践》

《Head First Design Patterns》

李建忠老师的《C#面向对象设计模式纵横谈系列课程》

本文标题:交互式系统原理与设计-如何利用交互设计的接近法则与格式塔理论
本文地址: http://www.61k.com/1141741.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1