前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java设计模式之策略模式及项目中的应用

java设计模式之策略模式及项目中的应用

作者头像
甲蛙全栈
发布2020-11-24 10:38:54
7720
发布2020-11-24 10:38:54
举报
文章被收录于专栏:Java全栈

转载请注意出处:http://blog.csdn.net/zcm101

设计模拟人生游戏

今天开始,我们LazyCoder准备着手开发一款模拟人生游戏,首先从设计人物开始,我们设想我们设计的人物可以讲话,吃东西,睡觉,他们的样子也都不一样。我们想到了继承,于是有了第一个类Person,之后我们再设计各种各样的人,家族里有很多人,有Father,Mother,Brother……他们的样子长得不一样,于是我们为每个人物设计一个类,他们都继承Person,并实现各自的display方法,display就交给美工们来做吧

。不会美工的童鞋举手,我第一个举手。

下面是我们第一个版本的代码:

代码语言:javascript
复制
package com.lazycode.v1;

public abstract class Person {

	public void sleep(){
		System.out.println("Person sleep");
	}
	
	public void eat(){
		System.out.println("Person eat");
	}
	
	public void speak(){
		System.out.println("Person speak");
	}
	
	public abstract void display();
}
代码语言:javascript
复制
package com.lazycode.v1;

public class Father extends Person {

	@Override
	public void display() {
		System.out.println("Father display");
	}

}
代码语言:javascript
复制
package com.lazycode.v1;

public class Mother extends Person {

	@Override
	public void display() {
		System.out.println("Mother display");
	}

}
代码语言:javascript
复制
package com.lazycode.v1;

public class Brother extends Person {

	@Override
	public void display() {
		System.out.println("Brother display");
	}

}

大家每天吃饱了睡,睡醒了吃,聊聊天,愉快的一天就过去了……

问题来了

随着模拟人生业务的发展,我们开始进军海外,于是,设计了个远房亲戚,来自美国的Sister,而我们现有的Person类,只支持讲中文。额—— 在Sister里覆盖下speak方法吧,让她讲英语。从此我们将暗无天日!随着人物的增多,我们每天都重复着为每个人加入讲英语的方法,而且是同一段代码,我要疯了,这可不是懒程序员的风格!而且每新建个人物时,我都得想一下,他应该讲中文还是讲英文呢,这我真的要疯了!!看来得改进我们的Person类了。

怎么办怎么办……

Person里加个国籍,在speak方法里加判断,根据国据,判断讲的语言? 疯了吧你!万一出现一个牛人,会八种语言怎么办?全世界上千种语言,你打算每加入一种新的语言就改下Person类吗,每改动一次Person类,对原有的代码就增加出风险的机率。再想想~~~

春天来了

我们发现所有中国人都讲汉语,所有美国人都讲英语,之后还会出现讲法语、德语的,我们可以每种语言都提炼成一个类,将speak提出成接口LanSpeak。一组语言类就这么出来了。

代码语言:javascript
复制
package com.lazycode.v2;

public interface LanSpeak {

	public void speak();
}
代码语言:javascript
复制
package com.lazycode.v2;

public class ChiSpeak implements LanSpeak {

	@Override
	public void speak() {
		System.out.println("Speak Chinese");
	}

}
代码语言:javascript
复制
package com.lazycode.v2;

public class EngSpeak implements LanSpeak {

	@Override
	public void speak() {
		System.out.println("Speak English");
	}

}

那么如何将Person类和LanSpeak接口联系起来呢?

我们再思考,Father见到Sister后,Sister不会讲中文,我们得让Father讲英文,也就是说Father在不同的场景中,能够中英文切换,试着将LanSpeak变成Person类里的一个属性(你一定听过面向接口编程,对的,我们在Person类里用到的时LanSpeak接口,而不是具体的ChiSpeak,EngSpeak。),在运行过程中,设置LanSpeak为不同的LanSpeak实现类,不就可以讲不同的语言吗。看下我们重新设计出来的Person类:

代码语言:javascript
复制
package com.lazycode.v2;

public abstract class Person {
	
	public LanSpeak lanSpeak;

	public void sleep(){
		System.out.println("Person sleep");
	}
	
	public void eat(){
		System.out.println("Person eat");
	}
	
	public void speak(){
		//输出具体讲话的人
		System.out.println(this.getClass().getName() + ":");
		lanSpeak.speak();
	}
	
	public abstract void display();

	public LanSpeak getLanSpeak() {
		return lanSpeak;
	}

	public void setLanSpeak(LanSpeak lanSpeak) {
		this.lanSpeak = lanSpeak;
	}
}

我们加入了个lanSpeak属性,set,get,在speak中, 不在是直接输出讲话信息了,而是 委托给了lanSpeak,要讲什么语言由他来决定。

再来看具体的人物,他们在被创建的时候,我们就默认给他设置一种语言,Father讲中文,USASister讲英文,所以在构造方法里需传入LanSpeak实现类。

代码语言:javascript
复制
package com.lazycode.v2;

public class Father extends Person {

	public Father(LanSpeak lanSpeak){
		this.lanSpeak = lanSpeak;
	}
	
	@Override
	public void display() {
		System.out.println("Father display");
	}

}
代码语言:javascript
复制
package com.lazycode.v2;

public class USASister extends Person {

	public USASister(LanSpeak lanSpeak) {
		this.lanSpeak = lanSpeak;
	}

	@Override
	public void display() {
		System.out.println("USASister display");
	}

}

好了,代码改造得差不多了,来看看怎么让Father和Mother讲中文,然后和USASister讲英文

代码语言:javascript
复制
package com.lazycode.v2;

public class Main {

	public static void main(String[] args) {
		//初始化语言
		ChiSpeak chiSpeak = new ChiSpeak();
		EngSpeak engSpeak = new EngSpeak();
		//初始化人物
		Father father = new Father(chiSpeak);
		Mother mother = new Mother(chiSpeak);
		USASister usaSister = new USASister(engSpeak);
		//father和mother交谈
		father.speak();
		mother.speak();
		//来自美国的妹妹插话了
		usaSister.speak();
		//让father讲英文
		father.setLanSpeak(engSpeak);
		father.speak();
	}

}

输出:

com.lazycode.v2.Father: Speak Chinese com.lazycode.v2.Mother: Speak Chinese com.lazycode.v2.USASister: Speak English com.lazycode.v2.Father: Speak English

策略模式就这么出来了,下面来看些理论上的东西吧。

概念

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

LanSpeak就是我们抽象出来的一组算法的接口,这组算法(ChiSpeak, EngSpeak)在程序运行的时候可以互相替换。

优点:很明显,扩展性很好;还有另一个隐性优点,这组语言类,脱离了Person,可以在其它地方用,比如广播。

缺点:很明显,扩展多了类就要爆棚了,而且客户端需要知道所有的算法,需要知道目前共实现了几种语言类。不过想比扩展性优点来说,这些还是可以容忍的。

策略模式一般会有三个角色:抽象策略角色(LanSpeak),具体策略角色(ChiSpeak),上下文(Person)

补充

回头再看看我们的代码,其实我们的Father类可以用v1包里的代码,不用做修改,因为我们的Person类里已经提供了SetLanSpeak方法了,每个人物都可以动态设置语言,而不用初始化就设置语言。我个人还是觉得这种写法会好点,我们现在只遇到了speak一组算法,以后可能会遇到move族算法,人物可以移动,但是每个人移动的方法不一样,有的是跑的,有的是走的,有的坐轮椅,有的骑自行车……还会有各种各样的算法族,难道每加一种算法族,所有的类的构造函数都改一遍?你这是要疯吧!!

项目实践

策略模式可以说是在项目中应用最多的模式之一,举一个最常见的例子,现在随便找个java项目,看看分层结构,是不是都会有一层service,一层dao,service里调用dao从而访问数据库。想一下,是不是有策略模式的影子了。

假设我们的项目有一个dao接口,叫CommonDao,有最基本的增删改查方法。我们再实现两个dao,分别是HibernateDaoImpl,JdbcDaoImpl,从名字就知道有什么区别吧。假如我们有一个PersonService,里面有个commonDao接口,我们通过Spring将HibernateDaoImpl注入到PersonService中。注意这个注入的过程,其实就是策略模式的体现,我们使用hibernateDaoImpl注入到PersonService,其实就是告诉service,我要使用hibernate来访问数据库这种策略。我们可以通过Spring的注入配置,来实现不同的策略。

假设哪天出现了个新的持久化框架,比如叫Lazycoder,那我们就可以实现LazycoderDaoImpl,然后将它通过Spring配置注入到PersonService中。

就做了两件事,增加一个策略,将这个策略代替旧的策略。So easy, 妈妈再也不用担心我的学习了。

思考

1. 随着业务需要,我们加入了德语,如何扩展?

2. 在补充里提到了给Person加入move动作,怎么重新设计Person类?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2013/07/17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 设计模拟人生游戏
  • 问题来了
  • 春天来了
  • 概念
  • 补充
  • 项目实践
  • 思考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档