设计模式初学者系列-策略模式 -------为什么总是继承

设计模式初学者系列-策略模式

                                                -------为什么总是继承

模板方法的延续

这篇稿子是基于我的前一篇模板方法设计模式之上演绎的,如果没有阅读请点击这里查看,以了解这篇稿子的上下文。

在模板方法设计模式里我举了一个例子:教育部规定了新生报到流程的算法骨架,然后这个算法骨架中的一些关键步骤由各高校自由的去发挥。我在这个例子中将高校设为一个抽象类,各高校要实现的算法步骤都是抽象方法。我还给出了两个高校的实现代码:清华大学和北京大学。在这个例子中本没有什么问题,但是软件总是会变的。

当有更多的高校要实现的时候,我们就会发现,很多高校的有些报到步骤实现是一样的,这就存在子类中有大量的重复代码,重复总是会出问题的。当然我们可以使用Martin Fowler的Pull Up Method(Refactoring P320)重构方法,将这些共同的部分推移到高校这个父类实现,并将这个抽象类改为virtual。

clip_image001

public abstract class 高校
{

public void 报到()

{

教务处报到();

缴费();

本院系报到();

教材科发教材();

}

protected abstract void 教务处报到();

//方法由抽象的更改为虚方法

protected virtual void 缴费()

{

//将这个方法在父类去实现,因为好多高校的实现都是这样的,避免重复

}

protected abstract 专业等信息 本院系报到();

protected abstract 教材 教材科发教材();

}

但是,现在出现了这样的情况:A,B,C等几个大学的实现在某些步骤上有些相同,D,F在某些步骤的实现有些相同,也许你会说:这不好办,继续使用继承呗,将共同的东西往上推,并且在“高校类”和各高校实现的类中间插入一些类,这些类将提供共同的实现。好像是个很好的办法。来瞧一瞧:

clip_image003

重复的代码确实减少了很多,但是还有一些重复(心里在默默的骂道:TMD,为什么C#不支持多继承,不然我就可以消除重复了),也许你还在自我陶醉的欣赏着自己多么完美的类继承层次,在那里感慨OO的强大。但是随着具体的高校越来越多,而且有的高校的报到步骤居然要发生改变,你小心的在中间那一层添加新的类,并将一些高校的实现转移,每一次你都非常小心(这个系统正在高速的运转,每改错一步,就有多少莘莘学子入不了学)。你心里终于对OO不满起来:为什么,为什么大家都说OO是救世主,但是却救不了我。答案是因为你将OO的设计原则遗忘在课本里了。开闭原则、优先使用组合,你还记得吗?

在我们很多OO程序员的脑子里总是存在这样一个观念:没有继承的程序不是OO的程序,看到重复总是想到继承。当初我也是这样想的,有的时候看到自己画的庞大的继承类图,心里在乐呵呵的笑。可继承总是不给面子,一个小小的变化就将这个看似稳定的体系弄的支离破碎。

还是回到我们的例子,在这个例子中变化的是各高校的报到步骤,本着发现变化、封装变化、隔离变化的原则我们将报到的步骤分离出来,独立成类。

clip_image005

这样我们就可以复用这些步骤了,有新的步骤实现只要添加更多的子类,并不需要修改原来的代码。(作业:在继续阅读之前根据上面的类图自己写出实现的代码来)

这就是所谓的策略设计模式:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于算法的客户(DP)。

策略模式有三种参与者:

一、 Context 这个类保存了对策略的引用,并且调用实际的策略实现,有可能还提供一个接口,让策略可以访问它内部的数据,在这里就是我们的“高校”类。

二、 Strategy 策略接口,给算法族定义一个通用的接口,让客户以一种一致的方法去访问。(I教务处报到,I缴费)

三、 ConcreteStrategy 这就是具体的策略实现了,实现策略接口(各报到步骤的实现)。

如下图:

clip_image006

在我们的例子中报到的步骤就是算法族 比如“缴费”这个步骤,有多种缴费方式,我们将其封装起来,客户调用的时候并不需要了解你是怎么实现这个“缴费”的,这个过程对于客户来说是透明的。这些不同的“缴费”步骤之间是可以无缝的替换,而客户对此一点都不知觉。

好了,既然解决方案提出来了,我们就来实现它吧

首先我们定义所有的报到步骤的接口:

public interface I教务处报到

{

void 教务处报到();

}

public interface I缴费

{

void 缴费();

}

public interface I本院系报到

{

void 本院系报到();

}

public interface I教材科发教材

{

void 教材科发教材();

}

下面我实现两个教务处报到的步骤,其他的就当作课后作业了,呵呵。

public class 教务处报到A : I教务处报到

{

public void 教务处报到()

{

Console.Write("教务处报到,A类实现");

}

}

public class 教务处报到B : I教务处报到

{

public void 教务处报到()

{

MessageBox.Show("教务处报到,B类实现");

}

}

再看看我们的高校类的实现吧:

public class 高校

{

public 高校(I教务处报到 p教务处报到,I缴费 p缴费,I本院系报到 p本院系报到,I教材科发教材 p教材科发教材)

{

this._教务处报到 = p教务处报到;

this._缴费 = p缴费;

this._本院系报到 = p本院系报到;

this._教材科发教材 = p教材科发教材;

}

//为什么有了赋值的构造函数还要暴露这么多只写属性出来呢?

//这样就可以在运行时改变高校的报到步骤了,

//假如报到系统出现故障我们可以马上采取另外一种方案

//而不需要停止系统的运行

I教务处报到 _教务处报到;

public I教务处报到 教务处报到

{

set

{

_教务处报到 = value;

}

}

I缴费 _缴费;

public I缴费 缴费

{

set

{

_缴费 = value;

}

}

I本院系报到 _本院系报到;

public I本院系报到 本院系报到

{

set

{

_本院系报到 = value;

}

}

I教材科发教材 _教材科发教材;

public I教材科发教材 教材科发教材

{

set

{

_教材科发教材 = value;

}

}

//用上了策略模式,模板方法更加灵活了

//但现在还是不是模板方法了?

public void 报到()

{

教务处报到.教务处报到();

缴费.缴费();

本院系报到.本院系报到();

教材科发教材.教材科发教材();

}

}

Ok,我就把代码写这么多了,要这个代码运行起来还需要一些补充,这个高校类如何进行实例化才能更灵活也值得考虑。

看到没,利用组合我们也可以达到代码复用的目的,而且没有继承的弊端。

上面好像都是在说策略模式的好话,那策略模式有没有副作用呢?当然有

一、 虽说客户代码无须关心各个策略是如何实现的,但是它们还是要知道有多少种策略实现,该实现是干什么的,也就是客户代码需要知道策略的一些细节,这样才可以根据需要使用哪个策略,但是我们可以使用创建型模式来解决这个问题。

二、 有的时候策略需要从Context那里获取一些数据,这样造成双向的关联,而且有可能几个策略需要的数据都不一样,但是为了一致性不得不向它们传递相同的数据。

三、 也许大家会发现,使用策略模式后出现很多小类,实际上这也是所有设计模式的“通病”。

现实中的策略模式

大家对于PetShop这个应用肯定很熟悉,在PetShop 4.0里面就使用了策略设计模式:

在Petshop4的BLL项目中有一个OrderAsynchronous类和一个OrderSynchronous类,它们都继承自IorderStrategy。OrderSynchronous以一种同步的方式处理订单,而OrderAsynchronous先将订单放在队列里,然后再对队列里的订单进行处理,以一种异步方式。而在BLL中的Order类里通过反射从配置文件读取策略配置的信息以决定到底是使用哪种订单处理方式。这里就不贴代码了,有兴趣的可以去下载PetShop看看,主要关注这几个:PetShop.BLL.Order(如何使用策略以及如何根据配置文件实例化具体的策略)、PetShop.IBLLStrategy. IorderStrategy(策略的接口)、PetShop.BLL. OrderSynchronous、PetShop.BLL. OrderAsynchronous。

总结

在本篇我们从模板方法谈起,聊了一些模板方法随着项目的发展可能造成的问题,但这并不是模板方法的弊端,模板方法关注的是算法骨架的复用,如果你发觉新的问题出现,这可能就是模板方法不再适用的信号。通过我们对项目的扩展,发现继承在某些时候并不是都能达到代码复用的目的,这个时候我们应该考虑组合了,而且继承是一种静态的编译期的行为(针对像C#这种强类型静态语言而言),代码一经写定我们就没有选择的余地了。

前几天和别人在群里闲聊,谈到怎样学习设计模式,有人说设计模式靠悟,有人说设计模式靠经验的积累。悟也好,经验积累也好,我的感觉是不要把设计模式当作圣经,当一个人把一个事物当作圣经的时候总是很珍惜她,而且不会去亵渎她,这是学习模式的障碍。对于初学者来说应该有“熟读唐诗三百首,不会吟诗也会吟”的决心。

转载于:https://www.cnblogs.com/yuyijq/archive/2008/01/14/1038286.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276965.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

红米airdots掉了怎么查找_红米K30 Pro 荣耀V30pro 这两款手机该怎么选呢?

点击?玩机数码君?关注我,加★星标★你好 我是岁月神偷昨天可以说是小米拍手称快的一天,红米K30 Pro以2999的超低价成为目前最便宜的骁龙865旗舰,让友商拍马难追。友商明眼人都知道说的华为,怎么感觉小米每次发布会也替华为宣传了…

返回一个循环整数组最大子数组和

任务要求: 1、输入一个整形数组,数组里有正数也有负数。 2、数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 3、如果数组A[0]……A[j-1]首尾相邻,允许A[i-1], …… A[n-1], A[0]……A…

4K 海思 联咏 芯片_老电视也有春天,换装海美迪4K电视盒子H7 Plus

写在前面YALL,大家好,我是老炮儿许老板。疫情进入六月逐渐好转,各级各类学校相继开学,年前给儿子报的托班也终于迎来了开学,平日里帮忙照看儿子的爷爷奶奶也终于得到了解放。现在白天有大把大把的时间来追剧看电视&…

WCF从理论到实践(4):路在何方

本文的出发点 通过阅读本文,您能了解以下知识: Address是什么? Address的组成? 如何在配置文件中指定Address? 如何通过编程方式设置Address? Address有什么特殊应用? 本文适合的读者 适合WCF初学者&#xff…

office 2007图标_微软Office 365桌面版新图标开始测试

IT之家3月1日消息 此前,微软公布了全新的Office图标,微软Office 365在线网页版在2月15日开始已经全面更新新版图标,而桌面版Office 365现在也陆续开始测试新版图标。目前微软Office Dogfood通道上推送的开发者预览版本已经在2月27日开始测试O…

【动态规划BFS】相遇

这是我第一次模拟题测试点全部AC。。。 同机房的DALAO都用的BFS 然而我用的DP(其实不会BFS) 话不多说,上题! (灰常详细)DP解法: 重点还是状态转移方程式的推导 1个点i要么是后面的位置i-1往前走…

五个思路,教你如何建立金融业的数据分析管理模型

说起银行、保险、股票投资这样的金融行业,很多人都认为它们是依靠数据驱动的企业,毕竟大数据的诞生本来就是为了金融信息流通而服务的,但在我身边很多搞证券、投资的朋友看来,事实却并非如此。 真正在金融行业做数据分析的人&…

【SSH网上商城项目实战19】订单信息的级联入库以及页面的缓存问题

购物车这一块还剩最后两个问题,就是订单信息的级联入库和页面缓存,这里的信息是指购物车和购物项,即我们将购物车的信息存入数据库的同时,也存入每个购物项的信息,而且外键都关联好,这涉及到了Hibernate中的…

exfat分配单元大小选多少_安防监控摄像机视角大小和镜头毫米数的基础知识!...

关于选择监控镜头毫米数的问题,虽然只有新手才有此困惑,但是我们还是要认真地说一说。监控视角,就是指监控照射的镜头所能覆盖到的范围,就是监控画面所能看到的角度统称叫监控视角。我们正常选购监控的时候,除了可以选…

彩信编辑器之预览功能

html代码 <table width"200"height"250"border"0"cellpadding"0"cellspacing"0"bgcolor"#666666"><tr><td align"center"valign"middle"><marquee id"MMScreen&qu…

java 几个实用的小工具

1、除法运算 编程的人都知道&#xff0c;java中的“/”、“%”运算&#xff0c;其中前者为取整&#xff0c;后者取余数。那么有没有快捷的运算方法取正常的运算结果呢&#xff1f; 查了资料&#xff0c;发现很简单。代码如下&#xff1a; public static String txfloat(int a,i…

动画演示 Delphi 2007 IDE 功能[3] - 修改属性

动画剧本:添加控件后用 F11 激活 Object Inspector 窗口;可用 ↑ ↓ 选择属性;用 Tab 切换属性名和属性值;用 Tab 切换到属性名后, 键入属性名的部分字母, 可迅速定位;用 Tab 切换到属性值后, 也可以键入字母选择, 而后回车确认.Ctrl↓ 可以选择其他控件;整个过程可以做到无鼠标…

kali怎么成为管理员_网站死链是什么、是怎么引起的以及死链对SEO优化的影响?...

网站死链是我们在做SEO时必不可少的一个错误&#xff0c;对于从事SEO行业的人员来说&#xff0c;网站死链最熟悉不过了&#xff0c;但是对于那些刚入SEO行业的新手来说&#xff0c;还是不太熟悉。今天我们就给大家讲一下什么是网站死链&#xff1f;网站死链是怎么引起的&#x…

Map-Reduce入门

1、Map-Reduce的逻辑过程 假设我们需要处理一批有关天气的数据&#xff0c;其格式如下&#xff1a; 按照ASCII码存储&#xff0c;每行一条记录每一行字符从0开始计数&#xff0c;第15个到第18个字符为年第25个到第29个字符为温度&#xff0c;其中第25位是符号/-006701199099999…

clickhouse大数据分析技术与实战_从销售到经营——大客户销售策略与实战技术...

对于首席客户代表而言&#xff0c;要走出困局&#xff0c;所需要大客户销售策略性的训练&#xff0c;而不是像基层客户经理的销售技巧训练一样&#xff1b;新业务的学习固然重要&#xff0c;但更重要的是转化成实战绩效。从组织变革角度&#xff0c;每次成功的业务转型背后都意…

Acer 4750 安装黑苹果_黑苹果系统安装通用教程图文版

在开始之前&#xff0c;不管你要安装的是台式组装机&#xff0c;台式品牌机&#xff0c;一体机&#xff0c;还是笔记本&#xff0c;都要大概了解一下硬件信息。因为黑苹果的安装确实比安装Windows的系统要复杂的多。不管是前期准备工作&#xff0c;安装&#xff0c;还是安装之后…

mysql 快速生成百万条测试数据

转自&#xff1a;http://www.cnblogs.com/jiangxiaobo/p/6101072.html 1、生成思路 利用mysql内存表插入速度快的特点&#xff0c;先利用函数和存储过程在内存表中生成数据&#xff0c;然后再从内存表插入普通表中2、创建内存表及普通表 CREATE TABLE vote_record_memory (id I…

java JVM

每一个Java虚拟机都由一个类加载器子系统&#xff08;class loader subsystem&#xff09;&#xff0c;负责加载程序中的类型&#xff08;类和接口&#xff09;&#xff0c;并赋予唯一的名字。每一个Java虚拟机都有一个执行引擎&#xff08;execution engine&#xff09;负责执…

给Domino系统管理员的十二项建议

Domino系统管理员的日常工作就是维护Domino系统的正常运行。以下简要说明了管理员所必做的一些工作。对于系统管理员&#xff0c;特别是新建系统的管理员来说&#xff0c;这些建议能帮助他们完成基本的维护工作。 根据许多资深的Domino管理员和咨询人员的经验&#xff0c;我们对…

技术管理—管理书籍推荐

技术出身&#xff0c;考虑接触下管理方面的知识。也许管理真的适合你&#xff0c;角色认知角色实践角色胜任&#xff01;最后爱上它&#xff01; 我最喜欢的一本书--高效能人士的七个习惯 作者&#xff1a;史蒂芬柯维&#xff08;Stephen Richards Covey&#xff09; 该…