记一场精彩的篮球比赛——浅谈策略模式

策略模式

声明:本文为原创,如有转载请注明转载与原作者并提供原文链接,仅为学习交流,本人才识短浅,如有错误,敬请指正

虽然我本人比较讨厌一些很官方的术语定义,因为我经常弄不明白有些定义讲了个啥,但是为了让这篇博文显得不那么轻浮,所以我也就不能免俗的先将设计模式之策略模式的定义首先丢到各位看官面前。

策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

第一眼这个定义看上去,难免会让人心生胆怯,又是算法族,又是封装,又是替换,似乎很复杂的样子,但是实际上,很好理解,不信,我们来一起看一场非常激烈的篮球比赛吧,相信看完这场比赛之后你就能大致掌握这个设计模式了。

PS:有人肯定要问了,为啥没有个UML图呢,其他人说设计模式都会咣当扔个UML图上来,你咋没有呢,是不是偷工减料,其实啊,真不是,在于我这个人呢,比较喜欢直接撸代码看代码,也没有说看UML图理解一个设计模式的,并不说UML图不重要,只是策略模式的UML图在网上一搜一大把,我还是不要当搬运工,复制粘贴了吧。

欢迎大家来到NBA赛场,今天的比赛呢,双方分别是湖人和骑士,科比与詹姆斯的宿命对决,作为篮球运动员,在场上,最重要的两件事是啥呢,没错,就是“投篮”和“传球”,投篮是为了得分,传球是为了更轻松的投篮得分,投篮有很多种方式,后仰投篮,三分远投,急停跳投,传球包括击地传球,不看人传球等,而在Java的设计模式中,我们可以把这些统统定义成“方法”。

我们首先抽象出来一个投篮相关的接口,它包含了一个方法:shoot(),即投篮

public interface ShootStrategy {

    public void shoot();
}

同样的,我们抽象出来一个传球相关的接口,它包含了一个方法:pass(),即传球

public interface PassStrategy {

    public void pass();
}

那么这两个接口有什么用呢,回到策略模式的定义,注意“算法族”这三个字,什么是算法族,如果后仰投篮是一个算法,三分远投是一个算法,急停跳投也是一个算法,我们就会发现他们的共同点是,他们都是投篮的算法,也就是说他们都是投篮的算法族,同理,击地传球与不看人传球也是传球的算法族,而后策略模式的定义说要分别封装他们,提到封装大家想到了什么呢,类!

接下来我们尝试封装一下后仰投篮算法和三分远投算法

public class BackwardShoot implements ShootStrategy {

    @Override
    public void shoot() {
        System.out.println("标志性的后仰跳投");
    }
}

封装后仰跳投算法,代码很简单,因为我们不希望算法的逻辑干扰到我们着眼于真正的重点——策略模式。

public class ThreeShoot implements ShootStrategy {

    @Override
    public void shoot() {

        System.out.println("神准的三分");
    }
}

封装三分远投算法。

同样的,我们也封装一下传球的算法,毕竟篮球离不开传球(科比:传球,传球是什么意思,误~)

public class DropPass implements PassStrategy {

    @Override
    public void pass() {
        System.out.println("诡异的击地传球");
    }
}

封装击地传球算法

public class NoLookPass implements PassStrategy {

    @Override
    public void pass() {
        System.out.println("一记精彩的不看人传球");
    }
}

封装不看人传球算法

好了,现在我们所有的策略都准备好了(原谅我这里直接使用了策略,不用惊吓,我们刚刚已经完成了策略模式最重要的部分),就差我们的运动员上场了。

我们首先定义一个篮球运动员的类。

public class BasketballPlayer {

    private String name;

    private ShootStrategy shootStrategy;
    private PassStrategy passStrategy;

    public void shoot(){
        System.out.println("------" + name + "------");
        shootStrategy.shoot();
    }

    public void pass(){
        System.out.println("------" + name + "------");
        passStrategy.pass();
    }

    public void setShootStrategy(ShootStrategy shootStrategy) {
        this.shootStrategy = shootStrategy;
    }

    public void setPassStrategy(PassStrategy passStrategy) {
        this.passStrategy = passStrategy;
    }

    public void selfIntroduction(){
        System.out.println("我是个篮球运动员");
    }

    public void setName(String name) {
        this.name = name;
    }
}

看一下这段代码,我定义了三个变量,name,表示运动员的名字,然后是我自己定义的投篮接口以及传球接口,这俩干干巴巴,麻麻赖赖的接口放这里有啥用呢,盘他,我们往下看,我们会发现,篮球运动员的投篮,使用的是我们投篮接口里的shoot()方法实现,而传球呢,使用的是我们传球接口里的pass()方法实现,有人就会想了,我这俩接口都没方法体,调用个毛啊,稍安勿躁,继续往下看,下面是两个平平无奇的setter方法,然而,奥秘就是在这里,这个奥秘,我们一般叫他“解耦”,通过使用Java的接口回调,我们可以将任意对象传入这个类中供这个类使用,只要这个类实现了对应的接口即可,在这里就是投篮接口与传球接口,然后我们就会发现,刚才我们实现的后仰投篮,三分远投等,这里统统受用!

说了之久,我们的大明星怎么还没露面呢,继续

科比是一名(is-a)篮球运动员,所以

public class Kobe extends BasketballPlayer {

    @Override
    public void selfIntroduction(){
        System.out.println("我的名字是科比");
    }
}

詹姆斯是一名(is-a)篮球运动员,所以

public class James extends BasketballPlayer {

    @Override
    public void selfIntroduction(){
        System.out.println("我叫詹姆斯");
    }
}

注意,虽然两个类代码比较少,但是不要忘了,由于他俩继承了篮球运动员类,所以篮球运动员里的那些方法,他们也都拥有。

好啦,现在万事俱备,让我们开始这一场激动人心的比赛吧

public class LakerVsCavalier {

    public static void main(String[] args) {

        Kobe kobe = new Kobe();
        kobe.selfIntroduction();
        kobe.setName("科比");

        James james = new James();
        james.selfIntroduction();
        james.setName("詹姆斯");

        kobe.setPassStrategy(new NoLookPass());
        kobe.setShootStrategy(new BackwardShoot());
        kobe.pass();
        kobe.shoot();

        james.setPassStrategy(new DropPass());
        james.setShootStrategy(new ThreeShoot());
        james.pass();
        james.shoot();

        kobe.setPassStrategy(new DropPass());
        kobe.setShootStrategy(new ThreeShoot());
        kobe.pass();
        kobe.shoot();

        james.setPassStrategy(new NoLookPass());
        james.setShootStrategy(new BackwardShoot());
        james.pass();
        james.shoot();
    }
}

虽然代码看起来很简单,只是让科比与詹姆斯两个对象shoot()与pass()而已,然而事情其实并没有那么简单,回顾策略模式的定义,“让它们之间可以互相替换”,这就是问题的关键,我们为同一个对象设置了不同的算法,科比可以先设置传球策略为不看人传球,设置投篮策略为后仰跳投,然后此封装的算法就会被科比对象中的pass()和shoot()方法调用,原因上文已述,然后,我们可以重新为科比对象设置不同的算法,我们把传球策略为击地传球,投篮策略设置为三分远投,然后科比再次传球(调用pass方法),就变成了击地传球,再次投篮(调用shoot方法),就变成了三分远投,詹姆斯对象同理。

我们来看一下执行结果

一切皆如我们所料。

至此,我们也就能理解策略模式定义的最后一句:此模式让算法的变化独立于使用算法的客户。

更重要的一点是,通过策略模式,我们真正的将易于变化的部分抽出来,使代码对修改关闭,对扩展开放,假设我们要添加一个新的策略——急停跳投,我们只需要通过实现投篮接口的方式新建一个急停跳投策略,然后把他set进科比,詹姆斯这种使用算法的对象即可,完全不用修改已有的调用算法的代码!

有兴趣的可以自己去实现看看哦

今天的篮球比赛转播到此结束,我是设计模式评论员JR,我们下次再见。

原文地址:https://www.cnblogs.com/MrJR/p/10441166.html

时间: 2024-08-29 05:13:40

记一场精彩的篮球比赛——浅谈策略模式的相关文章

浅谈三层模式

总觉的对三层的理解很肤浅,这几天看了相关的资料,无非谈的就是概括和基本组建附加个小例子!看完了,感觉说的大同小异,自己的理解好像也没什么多大变化,只不过加深了点罢了.不过想想有几天在这方面的思考,还是总结一下吧! 你去饭店吃饭,就遇见了三层,咱们唠唠吃饭这事! 服务员的作用就是给你上菜,收集你的信息,比如来个鱼香肉丝,或是几瓶啤酒,烤串什么的!总之你的一切请求都只是面向服务员的!至于厨师是男的,女的,负责给厨师买材料的采购员,你是没必要知道的.一切为了顾客,就是服务员的宗旨!等哪天这个服务员辞职

浅谈工厂模式

一个简单的计算器例子来描述工厂模式 Operator公共接口 package com.iss.factory; public interface Operator { public int getResult(int x, int y); } Add.java package com.iss.factory; public class Add implements Operator{ @Override public int getResult(int x, int y) { // TODO A

浅谈原型模式

近期菜鸟D在看<大话设计模式>,看到原型模式,有一点自己的想法,所以就记下来. 老规矩,解释定义: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建.                          ——来自百度 由于感觉

使用JavaScript浅谈组合模式

什么是组合模式? 从前有座山,山上住着一个老和尚和小和尚,老和尚给小和尚讲了一个故事,这个故事是这样子的:从前有座山,山上住着一个老和尚和小和尚,老和尚给小和尚讲了一个故事,这个故事是这样子的:从前有座山,山上住着一个老和尚和小和尚,老和尚给小和尚讲了一个故事,这个故事是这样子的... 骚年,读到这里不知道你明白了什么,一个故事由另一个故事组成,然后一直这样子组合下去,形成了一个最大的故事. 没错,这就是组合模式:小对象组合成一个大对象,小对象可能由更小的对象组合而成. 其实我们也可以看出两点:

浅谈开发模式及架构发展

一.传统开发模式 传统的开发模式基本一般是重服务端的开发方式,大部分工作都在服务端执行,然后返回到客户端(通常是HTML).以Asp.net MVC为例,如下图: #1 根据请求的路由定位到对应的Controller的对应的Action. #2 执行相关逻辑,得到结果Model(也可能没有Model,如直接返回View). #3 定位并加载对应的View(也可能没有View,如返回TextResult,JsonResult等). #4 在服务端通过Razor引擎把Model和View绑定起来生成

iOS:个人浅谈工厂模式

一.什么是工厂方法? 正式的解释是:在基类中定义创建对象的一个接口,让子类决定实例化哪个类.工厂方法让一个类的实例化延迟到子类中进行.工厂方法要解决的问题是对象的创建时机,它提供了一种扩展的策略,很好地符合了开放封闭原则.工厂方法也叫做虚构造器(Virtual Constructor). 个人的拙见是:通过工厂方法创建工厂对象,然后在工厂类中定义创建基类的子类对象的方法并通过外部传入的条件判断去创建哪一个子类对象,不过由于OC是运行时语言,所以工厂类虽然提供了创建子类对象的方法,但是在编译时期并

浅谈MVC模式

1. 如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architectural pattern),属于编程的方法论. MVC模式就是架构模式的一种,它对我的启发特别大.我觉得它不仅适用于开发软件,也适用于其他广泛的设计和组织工作. 下面是我对MVC模式的一些个人理解,不一定正确,主要用来整理思路. 2. MVC是三个单词的首字母缩写,它们是Model(模型).View(视图)和Controller(控制). 这个模式认为,程序不论简单或复杂,从结构上看,都可以分成三层.

Android开发:浅谈MVP模式应用与内存泄漏

最近博主开始在项目中实践MVP模式,却意外发现内存泄漏比较严重,但却很少人谈到这个问题,促使了本文的发布,本文假设读者已了解MVP架构. 本文原创作者:xiong_it,链接:http://blog.csdn.net/xiong_it MVP简介 M-Modle,数据,逻辑操作层,数据获取,数据持久化保存.比如网络操作,数据库操作 V-View,界面展示层,Android中的具体体现为Activity,Fragment P-Presenter,中介者,连接Modle,View层,同时持有modl

Swift语言精要 - 浅谈代理模式(Delegate)

在iOS编程中,我们经常谈到代理代理,也就是delegate,那么什么是代理呢? 我们来看一下cocoa对它的描述: Delegation is Cocoa’s term for passing off some responsibilities of an object to another 顾名思义: 代理是把职责从一个对象传到另一个对象. 其实,如果编写过Java或者C#程序的朋友应该知道,我们在设计的时候经常使用到接口和组合,道理是一样的. 看一下下面的代码: 首先,我们定义一个协议 p