Head First设计模式读书笔记——策略模式

问题描述

       目前的任务是实现一个FPS类游戏的各种角色(友军、敌军、平民和狗、猫、鸭子等动物)以及他们的各种行为(攻击、游泳等)。

设计方案一

       很简单,只要实现一个角色超类,将角色的各种行为放入超类中,其他具体角色都继承次角色超类就可以了。类图如下所示(由于不同的角色有不同的外观,所以display设定为抽象方法,由子类自己实现)。


       现在想想,这个设计有什么潜在的问题?对了,游戏开发完成后,你在享受游戏的乐趣的同时可能会发现竟然有一只猫在游泳,还有一只鸭子在向友军攻击...。这些是有悖常理,绝对不允许出现的。也许会有人想到一种解决方案,就是在子类中覆盖父类中相应的方法。但是这样一来,如果系统需要加入新的角色,程序员必须跟踪并可能覆盖fightswim方法,永无止境的噩梦。必须更改设计方案。

设计方案二

       针对上一个方案出现的问题,首先将会变化的行为(fightswim)抽出来,各自放入单独的接口fightActionswimAction中,只有具有此类行为的角色才会实现相应的接口。设计图如下:


       稍稍动动脑筋就会发现,这种设计方案其实超傻。如果有成千上万种角色,就意味着程序员要重复编写成千上万次同样的代码。完全消除了代码复用的优势,几乎可以荣登史上最傻方案榜了。毫不犹豫,淘汰,必须更改方案。

设计方案三

       在上一篇文章面向对象设计原则中的第一个原则是“封装变化”,这个原则可以理解为:“找出应用程序中需要变化的部分,把它们独立出来,不要和那些不需要变化的代码混在一起,以便以后可以轻易的改动或扩充此部分而不至影响到其他部分。”基于此,把角色的行为从角色中抽离出来。

       为了实现“封装变化”,首先建立两组类,其中一个关乎fight行为的,另一个是关乎swim行为的,各自实现各自的行为,并将该两组类完全脱离角色类。

       以上一切工作都是为了使得程序更具有弹性,易于扩展。所以,当然不能让子类来实现设定行为的方法,必须放在父类中。

       ”面向对象设计原则中的第三个原则是“针对接口编程,而不是针对实现编程”,注意,此处讲到的接口并不是特指某个C#java的接口,而是泛指实现某个超类性(类或接口)的某个方法。在此设计方案中利用接口代表行为,fight行为用FightAction接口代表,swim行为用SwimAction接口代表,这样角色本身不会负责实现某个行为,而是由专门的“行为类”(实现行为接口的类)来负责。设计方案如下图所示:


       父类Character中用performFightperformSwim方法代替了方案一和方案二中的fightswim方法。在performFight方法和performSwim方法中调用fightActionswimAction中的fightswim方法实现角色的行为。而每一个角色子类的fightActionswimAction成员是在角色本身实例化的时候指定的。利用此方案实现的系统,决不会出现具有攻击行为的鸭子和会游泳的猫,同时也保证了系统的可扩展性,有新的行为加入的时候(如fightWithKnife)只需要添加一个新的行为类即可以实现。程序员甚至可以在父类中添加一个setFightActionFightAction fightAction)或setSwimActionSwimAction swimAction)方法来实现每一个角色在运行的时候动态的改变行为(如用枪攻击改为用刀攻击)。

总结

       以上陈述的“设计方案三”利用的既是策略模式,有心的你也许会发现,该方案同样遵循了面向对象设计原则中的第二个原则是“多用组合,少用继承”。每一个角色都具有一个fightActionswimAction用于委托处理fight行为和swim行为,这就是组合。“组合”不同于“继承”之处在于角色的行为不是继承来的,而是由行为对象组合而来。

       引用策略模式的官方定义:“策略模式定义了算法族,分别封装起来,让它们直之间可以互相替换,此模式让算法的变化独立于使用算法的客户”。不难发现,该模式主要适用于不同客户(角色)使用不同策略(行为),并且策略本身将来可能通过不同方式实现,需要对客户隐藏具体策略的实现细节,完全独立的情况。策略模式提供了一种替代继承的方法,既保持了继承的优点(代码复用),同时也比继承更加灵活(算法独立,可任意扩展)。该模式还避免了多重条件语句的出现,使系统更易于扩展和维护。

转载于:https://www.cnblogs.com/NeeoMeng/articles/1119250.html

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

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

相关文章

centos+bond+bridge+docker(ssh容器)固定ip实现测试环境(一)

硬件:R730交换机:H3C Switch S5120-28P-SI系统:centos7#nmtuihttp://568273240.blog.51cto.com/802.3ad为LACP模式交换机部分:# systemctl restart network可以多重启几遍试下。http://568273240.blog.51cto.com/注意:…

jQuery 获取屏幕高度、宽度

做手机Web开发做浏览器兼容用到了,所以在网上找了些汇总下。alert($(window).height()); //浏览器当前窗口可视区域高度 alert($(document).height()); //浏览器当前窗口文档的高度 alert($(document.body).height());//浏览器当前窗口文档body的高度 alert($(do…

python 发起HTTP请求

因为微信公众号群发需要调用高级群发接口,其中涉及到python发起HTTP请求,现在将相关实现操作记录如下: 首先,HTTP请求分为GET和POST,如下所示: 首先是发起get 请求: # -*- coding: utf-8 -*- im…

如何在webservice中取得sesssionid

[WebMethod(EnableSessiontrue)] 一定要标注EnableSessiontrue public string GetID() { return Session.SessionID; } 转载于:https://www.cnblogs.com/suneryong/archive/2008/03/26/1122578.html

pthread_join

函数简介 函数pthread_join用来等待一个线程的结束。函数原型为: extern int pthread_join (pthread_t__th, void **__thread_return); 第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。…

简单线性回归算法

为什么80%的码农都做不了架构师?>>> /*** 简单线性回归算法* param array y轴数据* param array x轴数据* returns array(slope,intercept,r2)*/ function linearRegression(y, x) {var lr {};var n y.length;var sum_x 0;var sum_y 0;var sum_xy …

模拟BS服务器

一、模拟BS服务器分析 二、BS模拟服务器代码实现 图片都是单独请求,后台单独线程,这边是通过构造方法传入的Runable接口的实现类匿名对象创建线程; 创建本地输入流读取到网络输出流传过来的信息再放到网络输出流中返回; 转载于:ht…

c#修饰符!

在C#中,有如下几种修饰符,分别是public,protected,internal,private。先来看一下C# 4th Edition中的说明: public No restrictions. Members marked public are visible to any method of any class. private The members in class A that ar…

wifi定位技术

wifi定位是iphone升级到1.1.3之后新加的应用服务,拿到机器后开始试用一下wifi定位。在美国达拉斯,定位按钮被按下后,旋转了大约2秒钟,所在的街道就立刻闪现在屏幕中央了。难怪乔布斯也说:“Its really cool”&#xff…

linux定时任务启动

/sbin/service crond stop /sbin/service crond start /sbin/service crond restart

不要62

题目 试题描述杭州人称那些傻乎乎粘嗒嗒的人为 62(音:laoer)。杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除…

myssh

http://blog.csdn.net/infon/archive/2007/04/17/1568309.aspx http://blog.csdn.net/infon 转载于:https://www.cnblogs.com/tries/archive/2008/04/11/1148121.html

队列,排队买饭

1 /*这题的错误是和同学熬夜找出来的呃 。。。*/ 2 3 #include<stdio.h> 4 #include<string.h> 5 int main() 6 { 7 int front,rear,i,f,j,n,t,q[10000]; 8 char s[20]; 9 scanf("%d",&n); 10 rear 0,front0; 1…

CentOS 7 下的 Firewall

CentOS 7 默认实用的用Firewalld作为防火墙&#xff0c;摒弃了原先的iptables。但是内核还是使用iptable作为管理参考文档https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Using_Firewalls.htmlhttp://www.myhome.net.tw/2…

POJ 1091(数论)

题目大意是给定两个整数n和m&#xff0c;求出长度为n1满足条件的数列data的个数&#xff0c;数列的要求下&#xff1a;1&#xff09;1<data[i]<m,for1<i<n2)data[n1]m;3&#xff09;这个n1个数满足&#xff1a;存在x1,x2,...,xn,xn1,满足x1*data[1]x2*data[2]...x(…

你真的了解.NET中的String吗?

你真的了解.NET中的String吗? Terrylee&#xff0c;2005年12月25日 概述 String在任何语言中&#xff0c;都有它的特殊性&#xff0c;在.NET中也是如此。它属于基本数据类型&#xff0c;也是基本数据类型中唯一的引用类型。字符串可以声明为常量&#xff0c;但是它却放在了堆中…

没有动任何配置文件,今天就出现了修改的JSP内容在页面不体现。依然是老的页面内容...

2019独角兽企业重金招聘Python工程师标准>>> 现象&#xff1a; 没有动任何配置文件&#xff0c;今天就出现了修改的JSP内容在页面不体现。依然是老的页面内容 问题分析: 既然可以用过&#xff0c;所以系统应该是检测文件更新的&#xff0c;之所以不重新编译JSP&…

DIV中class和id的区别

1.class是设置标签的类&#xff0c;id是设置标签的标识;2. 使用方法不同&#xff1a; class属性用于指定元素属于何种样式的类,如样式表可以加入.content1 { color: red; background: #ff80c0 } 使用方法&#xff1a;class"content1" id属性用于定义一个元素的独…

新浪微博api(.net)时间格式问题

Tue May 03 09:30:23 0800 2011 以前研究sinaapi的时候被这个时间格式纠结了好久&#xff0c;还自己写了一个函数去处理这种格式。最近在网上看到一个代码&#xff0c;很方便就解决了这个问题&#xff08;我就说.net怎么会没有这方法呢&#xff09;。 System.Globalization.Cul…

php代码上线,实现版本切换

以下为现有php业务&#xff0c;代码上线方式。实现&#xff1a;4套环境版本切换。[roothz-web-01 htdocs]# cat release.sh #!/usr/bin/env bashwork_dir/mnt/var/www/htdocs release_dir/var/www/htdocs# app environment app_env$1 # build release file path build_file_pat…