声明:原创作品,转载请注明出处https://www.jianshu.com/p/4d53aa2adb22
在开始说设计模式之前,让我们先聊点别的东西,相信每个人都有自己难忘的童年时光,在那时有很多有趣好玩的事物陪伴着我们。我记得小时候,很喜欢看一部动画片--《四驱兄弟》,,,好吧暴露年纪了。。受动画片的影响,还经常和小伙伴们一起买四驱玩具车玩。除了玩具车外,我还喜欢玩游戏机来打发时间,当然不是你想的那种游戏主机什么的,而是这种:
。。可能现在有些小伙伴不认识这家伙了,这其实就是一个手持的小型游戏机,里面内置了几种游戏,我能够想起的几种游戏一个是比较经典的俄罗斯方块,还有个是很简单的赛车游戏,就是在游戏中不断躲避障碍物,并且速度还会不断加快。种类还是比较少的。好了言归正传,一般工厂生产产品时,都需要一个模具,或者说一套标准,在生产具体的产品时,都得照着这个标准做。好了,那么生产这个游戏机的标准是怎样的呢,我们可以定义一个类来描述这个标准:
/**
* 工厂中制作游戏机的一个模型标准(类似于模具)
*/
public abstract class GamePlayerModel {
// 每个游戏机都内置一款游戏,具体游戏类型在生产具体的游戏机时装入
abstract void game();
// 游戏机外观,具体在生产时决定
abstract void display();
// 点击游戏机上的开始游戏按钮进入游戏
public void startGame(){
game();
}
}
可以看到,在这个游戏机模具中,内置了一款游戏(这里假设每个游戏机中只有一款游戏),当然开始什么也没装,只有在生产具体类型的游戏机时才会装入相应的游戏,还有个开始游戏的方法,当点击开始游戏就会调用内置的游戏。里面还有个display方法,表示游戏机的外观,具体是什么颜色,也只有在生产时才能决定。当然这里的模型我们是经过简化的,事实上要复杂很多。
好了有了这个游戏机模型,我们生产一个游戏机就很方便了,比如我们需要生产一个红色的内置俄罗斯方块游戏的游戏机,我们定义一个类来描述:
public class RedTetrisPlayer extends GamePlayerModel{
@Override
void game() {
System.out.print("正在俄罗斯方块游戏中...");
}
@Override
void display() {
System.out.print("一台红色的游戏机");
}
}
可以看到,有了之前游戏机模型标准的基础,我们需要一台有俄罗斯方块游戏的游戏机时,在生产时装入这款游戏并给游戏机上了颜色。
接下来我们就试试这款游戏机好不好用:
public class TestClient {
public static void main(String[] args){
// 一台新鲜出炉的红色的游戏机
RedTetrisPlayer tetrisGamePlayer = new RedTetrisPlayer();
// 点击游戏机上的开始游戏按钮
tetrisGamePlayer.startGame();
}
}
代码执行结果:
---------------------------------------------------------------------------------------------------------------------
正在俄罗斯方块游戏中...
---------------------------------------------------------------------------------------------------------------------
可以看到,俄罗斯方块游戏已经启动,是不是有点激动。。。
从此以后,我便迷上了这个游戏,只要一有空我就会掏出游戏机撸上几局。然而。。。一直玩这个游戏,慢慢我也产生厌倦了,我想换个游戏玩,可是这个游戏机里就一个游戏,怎么办呢。。此时,商店里还有很多装了不同游戏类型的游戏机,我的第一反应就是再去买一个,可是一想,再买一个又得花钱,当时的价格对于我来说简直是天价,毕竟有个5毛一快的,那时候在小伙伴中都可以算个“有钱人”。。。再说即使买了,要是一个两个还好,要是好几个放家里,也是碍事。这时,我突然想到邻居一位大哥哥平时挺喜欢捣鼓这些电子器件的,就想着能不能让他帮我把游戏改改,果然,他说可以,不过操作上还是比较麻烦的需要把机子拆开,然后把游戏代码重新写到设备上,最后还得组装起来。虽然这花了几天时间,但好歹我又有新游戏可以玩了。然而再好玩的游戏也会有玩腻的一天,一段时间后,我又想玩新的游戏了,我第一反应就是去找哪位大哥哥,可是转念一想,改个游戏没想象中的那么简单,每次都需要把机子拆了,重新写程序,再重新组装,想到这些,我又打消了念头。就这样,我把游戏机扔在了一旁,专心看起了我的动画片。直到有一天,我看见小伙伴中有人在玩这个:
我们当时管这叫“gameboy”,也是一种手持游戏机,不过要比之前的那种高端,他的背部有个插槽,可以插入不同的游戏卡。
卧槽!这不正是我需要的吗,想要玩什么游戏,就插入什么卡就行了,简直不要太爽好吧。。
好了,既然出现了个新家伙,我们来看看用代码怎么描述这个它,我们刚才看到比起之前的游戏机它的背部多了个插槽或者叫做接口,用于插入游戏卡。厂家在制作游戏卡时,也会准从一套标准,最主要的标准就是游戏卡上的接口类型要和游戏机上的一致,比如游戏机插槽有几个管脚,那么游戏卡的接口处就得做几个管脚,总不能在这游戏机上插入小霸王的卡把。。除了游戏卡的接口处要和游戏机上的插槽一致外,游戏卡中最重要的就是要有游戏啦,不然要这卡干嘛。这样,只要制作游戏卡的时候都遵循这个标准,那么含有不同游戏的卡就都能插入到游戏机上。好了我们来看下代码:
/**
* 游戏卡制作的标准,插槽接口处类型为GameBoyKard
*/
public interface GameBoyKard {
// 卡里装的游戏,生产时才装入
void game();
}
同理,生产GameBoy也有一套标准:
public abstract class GameBoyPlayerModel {
// 游戏机上装有一个类型为GameBoyKard的插槽,表示只允许GameBoy的卡插入
private GameBoyKard mGameKard;
// 游戏机的外观,有生产过时决定
public abstract void display();
// 用于插入游戏卡
public void setGameKard(GameBoyKard gameKard){
this.mGameKard = gameKard;
}
// 开始游戏按钮
public void startGame(){
mGameKard.game();
}
}
我们可以看到,比起之前的那个游戏机,这个gameboy游戏机已经没有内置的游戏了,而是多了一个插槽,用于插入游戏卡。有了这套标准,我们生产一台gameboy游戏机就方便多了,比如我们也想生产一台红色的:
public class RedGameBoyPlayer extends GameBoyPlayerModel{
@Override
public void display() {
System.out.print("这是一台红色的GameBoy");
}
}
也是很简单,基于之前定义的标准,我们只要生产时给定颜色就可以了。
这样,我们的游戏机就有了,不过里面可没装任何游戏,我们还需要一张游戏卡:
public class TetrisGameCard implements GameBoyKard {
@Override
public void game() {
System.out.print("正在俄罗斯方块游戏中...");
}
}
可以看到我们定义了一张装有俄罗斯方块的游戏卡,当然这张卡的设计标准都是遵循GameBoyKard的标准。
好了,游戏机和游戏卡都已经就绪,赶紧来试下吧:
public class TestClient {
public static void main(String[] args){
// 生产一台红色gameboy
RedGameBoyPlayer redGameBoyPlayer = new RedGameBoyPlayer();
// 生产一张俄罗斯方块游戏卡
TetrisGameCard tetrisGameCard = new TetrisGameCard();
// 向游戏机中插入游戏卡
redGameBoyPlayer.setGameKard(tetrisGameCard);
// 开始游戏
redGameBoyPlayer.startGame();
}
}
代码执行结果:
---------------------------------------------------------------------------------------------------------------------
正在俄罗斯方块游戏中...
---------------------------------------------------------------------------------------------------------------------
怎么样,游戏跑起来,还不错把,可能你想玩点其他游戏,没关系,我们只用换张卡就行了,比如有一张赛车的游戏卡:
public class CarGameCard implements GameBoyKard {
@Override
public void game() {
System.out.print("正在赛车游戏中...");
}
}
只要把这张卡插入游戏机中,就可以更换游戏了:
// 生产一张赛车游戏卡
CarGameCard carGameCard = new CarGameCard();
// 向游戏机中插入游戏卡
redGameBoyPlayer.setGameKard(carGameCard);
// 开始游戏
redGameBoyPlayer.startGame();
代码执行结果:
---------------------------------------------------------------------------------------------------------------------
正在赛车游戏中...
---------------------------------------------------------------------------------------------------------------------
怎么样,是不是感觉gameboy游戏机的这种设计比之前要方便很多。其实这种设计方法正是运用了我们今天的主角--策略模式。好了来看下它的定义:
策略模式 -- 定义算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
定义可能有点抽象,不过想想我们这个游戏机的例子,这里的算法族可以理解为某个游戏,我们用游戏卡封装游戏,这样游戏就从游戏机中独立出来,并且不同的游戏卡可以互相切换,这样你再看下定义是不是就好理解多了呢?
那么这种设计模式对我们代码来讲有哪些优势呢?在上文一开始介绍的游戏机,如果我们想换个游戏玩,有两种方式,第一种,就是再去买个游戏机,但是为了换个游戏,我又多了个游戏机,,我们可以看到游戏机的类都是GamePlayerModel这个抽象类的子类,采用这种方式,必定会造成大量的子类以及重复代码。第二种方式就是直接改写游戏机中的游戏,这种方式也是不推荐的。如果直接在代码中修改,一来比较麻烦,再者,这样频繁修改很容易会影响其他功能模块,对项目的维护性和扩展性大打折扣。相反,我们在看看使用了策略模式的游戏机,我们不再需要定义大量不同的游戏机子类,我们要改变的是游戏,只用创建不同游戏卡类就行了,而且不同的卡之间可以互相切换,对原来的功能模块也不会产生任何影响。
另外在策略模式中我们也可以看出几条设计原则,什么是设计原则呢?我们都知道,每种编程语言都有语法,如果你在编程时不遵守语法,那么编译器就会报错。就好比法律,如果你不守法,那么就会得到制裁。除了守法外,我们都是讲道德的,这是要高于法律的。这里的设计原则就像是我们的品德,当然你可以不遵守这些设计原则,只要编译器不报错就好了,不过我相信大家都是讲道德的人,为了不让我们的代码去坑别人,我们还是来看看设计原则吧。在策略模式中,包含了这几条设计原则:
1.在代码中把那些可能变化的地方分离开来,别让它和不变的代码混在一起。
2.针对接口编程,而不是针对实现编程。
3.多用组合,少用继承。
相信从字面上也是很好理解,我们再结合上面的例子简单讲下。第一条,在最开始的游戏机类中,有两个方法,游戏和外观,一般游戏机生产出来外观就不会有变化了,而我们想改变的是里面的游戏,所以在gameboy游戏机中,游戏从游戏机中分离了出来。第二,在之前的游戏机中,游戏是直接实现在游戏机类中,而gameboy则是用到游戏的接口。第三条,顾名思义,每个gameboy游戏机都能和不同的游戏卡任意组合,当然这个游戏卡,要实现相应的接口。而之前的游戏机类都是继承于父类,里面的游戏也就写死了。
好了,这篇文章到这也就差不多了,相信你已经很好的掌握了策略模式以及其中体现的几点设计原则。如果哪天突然忘了什么是策略模式,就想想小时候玩的游戏机吧!
设计模式持续更新中。。。