策略模式实战 - 猜拳游戏

**可以整体的替换一套算法,这就是策略模式。**这样对于同一个问题,可以有多种解决方案——算法实现的时候,可以通过策略模式来非常方便的进行算法的整体替换,而各种算法是独立封装好的,不用修改其内部逻辑。

具体的实战,下面给出一个经典案例——“猜拳游戏”。该示例来自于【日】结城浩的《图解设计模式》,策略算法做了一些简化调整。

“石头剪刀布”的游戏每轮出什么样的手势,可以遵循一定的策略。比如可以按照下面两种策略来出手势:

  1. 看上一轮自己出的手势,如果赢了,继续用上一轮出的手势;否则出任意的手势
  2. 看上一轮对方出的手势
    1. 如果赢了,就出和上一轮不一样的手势;
    2. 如果上一轮平了(比如出的剪刀),本轮就出石头;
    3. 如果上一轮输了,本轮就出上一轮和对方一样的手势

文章目录

    • 整体类图设计
    • 出手势策略接口
    • 手势类
    • 玩家类
    • 策略实现类
    • 测试类

整体类图设计

在这里插入图片描述

出手势策略接口

因此抽象出一个出手势的策略接口:

/**
 * Java小卷带你轻松高效学编程,一对一辅导加q1372569394
 */
package com.juan.java.designpattern.strategy;

/**
 *
 * @author Java小卷
 * @date 2024-12-04 14:16
 * @since 1.0
 */
public interface Strategy {

    /**
     * 下一回合出手势的方法
     * @author Java小卷
     * @date 2024/12/5 14:04
     * @return 出的手势
     * @since 1.0
    */
    Hand nextHand();

    /**
     * 仔细考虑上一轮的结果,作为下一轮出手势的策略的依据
     * @author Java小卷
     * @date 2024/12/5 14:07
     * @param result 上一轮的结果 0-打平 1-胜 -1-负
     * @param other 上一轮对方的手势
     * @since 1.0
    */
    void study(int result, Hand other);

}

在这里插入图片描述

手势类

封装了手势具体信息和比手势的方法。

在这里插入图片描述

这里会维护3个公开的静态常量来分别维护“石头、剪刀、布”的手势数值。

/** 出的石头 */
public static final int HANDVALUE_STONE = 0;
/** 出的剪刀 */
public static final int HANDVALUE_SCISSORS = 1;
/** 出的布 */
public static final int HANDVALUE_PAPER = 2;

为方便对三种手势对象的获取,这里的手势值和三种手势对象的数组的索引值保持一致。

Hand类中维护一个私有的静态数组常量,初始化3个手势对象:

/** 初始化三种手势:石头、剪刀、布 */
private static final Hand[] HANDS = {
        new Hand(HANDVALUE_STONE),
        new Hand(HANDVALUE_SCISSORS),
        new Hand(HANDVALUE_PAPER)
};

同时维护一个对应手势名称的静态数组常量:

/** 描述出的手势的名称数组 */
private static final String[] HAND_NAMES = {"石头", "剪刀", "布"};

提供一个代表所出的手势值的成员变量,并提供相应的构造方法完成其初始化,注意Hand类不能在外部实例化,只能在内部维护,因此用private修饰:

/** 出的当前手势值 */
private final int handValue;

/** 私有的带有手势值的构造 */
private Hand(int handValue) {
    this.handValue = handValue;
}

提供几个获取手势信息的方法:

/**
 * 根据手势值获取对应的手势对象
 * @param handValue 手势值
 * @return 对应的手势对象
 */
public static Hand getHand(int handValue) {
    return HANDS[handValue];
}

/**
 * 返回当前实例的手势名称
 * @author Java小卷
 * @date 2024/12/4 10:57
 * @return 手势名称
 * @since 1.0
*/
@Override
public String toString() {
    return HAND_NAMES[handValue];
}

/**
 * 获取手势数值
 * @author Java小卷
 * @date 2024/12/5 15:14
 * @return int 手势数值
 * @since 1.0
*/
public int getHandValue() {
    return handValue;
}

提供比较手势的方法

/**
 * 手势对战方法
 * @author Java小卷
 * @date 2024/12/4 10:42
 * @param hand 对方出的手势
 * @return int 0-平局 1-胜 -1-负
 * @since 1.0
*/
private int fight(Hand hand) {
    // 自身比较无意义,但这里也记为平局
    if (this == hand) {
        return 0;
    } else if ((this.handValue + 1) % 3 == hand.handValue) {
        // 游戏规则:石头>剪刀>布>石头
        // 因此,只要按照数组的顺序,当前手势的下一个元素与对方手势相等,就认为自己赢了
        return 1;
    } else {
        // 其他情况都是判负
        return -1;
    }
}

/**
 * 对外提供的判断赢了方法
 * @author Java小卷
 * @date 2024/12/4 10:55
 * @param hand 对方手势
 * @return boolean
 * @since 1.0
*/
public boolean isStrongThan(Hand hand) {
    return this.fight(hand) == 1;
}

玩家类

Player类在策略模式的类设计中,作为Context,由它负责设置和切换策略并调用策略的行为。

该玩家类除了namestrategy属性外,还包含了全局的状态信息(比赛总轮数、胜的轮数、败的轮数)。另外提供了调用策略对象完成出手势的方法以及对一轮比赛结果处理的方法。

在这里插入图片描述

策略实现类

正如前面一开始介绍的出手势的两种实现策略,这里提供两种具体的实现:

/**
 * Java小卷带你轻松高效学编程,一对一辅导加q1372569394
 */
package com.juan.java.designpattern.strategy.impl;

import ...

/**
 *
 * @author Java小卷
 * @date 2024-12-04 14:21
 * @since 1.0
 */
public class WinningStrategy implements Strategy {

    private final Random random;

    private int prevResult;

    private Hand prevHand;

    public WinningStrategy() {
        random = new Random();
    }

    @Override
    public Hand nextHand() {
        // 前一轮不胜,则随意出
        if (prevResult != 1) {
            prevHand = Hand.getHand(random.nextInt(3));
        }
        // 否则出上一轮的手势
        return prevHand;
    }

    @Override
    public void study(int result, Hand other) {
        this.prevResult = result;
    }
}
/**
 * Java小卷带你轻松高效学编程,一对一辅导加q1372569394
 */
package com.juan.java.designpattern.strategy.impl;

import ...

/**
 *
 * @author Java小卷
 * @date 2024-12-04 15:38
 * @since 1.0
 */
public class SmartStrategy implements Strategy {

    private final Random random;

    private int prevResult = 1;

    private Hand prevOtherHand;

    public SmartStrategy() {
        this.random = new Random();
    }

    @Override
    public Hand nextHand() {
        // 如果前一轮平,则出比它大的
        if (prevResult == 0) {
            return Hand.getHand(Math.floorMod(prevOtherHand.getHandValue() - 1, 3));
        } else if (prevResult == -1) {
            // 输了则出和对方一样的
            return Hand.getHand(prevOtherHand.getHandValue());
        } else {
            // 赢了,则出不一样的
            if (prevOtherHand == null) {
                return Hand.getHand(random.nextInt(3));
            } else {
                Hand prevHand = Hand.getHand(Math.floorMod(prevOtherHand.getHandValue() - 1, 3));
                // 出和prevHand不一样的随机手势
                return Hand.getHand(Math.floorMod(prevHand.getHandValue() + random.nextInt(2) + 1, 3));
            }
        }
    }

    @Override
    public void study(int result, Hand other) {
        this.prevResult = result;
        this.prevOtherHand = other;
    }
}

测试类

/**
 * Java小卷带你轻松高效学编程,一对一辅导加q1372569394
 */
package com.juan.java.designpattern.strategy;

import ...

/**
 *
 * @author Java小卷
 * @date 2024-12-04 14:46
 * @since 1.0
 */
public class Main {

    public static void main(String[] args) {
        Player player1 = new Player("糖宝", new WinningStrategy());
        Player player2 = new Player("小卷", new SmartStrategy());
        // 比赛10轮
        for (int i = 0; i < 10; i++) {
            Hand hand1 = player1.nextHand();
            Hand hand2 = player2.nextHand();
            if (hand1.isStrongThan(hand2)) {
                System.out.println("胜者: " + player1);
                player1.win(hand2);
                player2.lose(hand1);
            } else if (hand2.isStrongThan(hand1)) {
                System.out.println("胜者: " + player2);
                player2.win(hand1);
                player1.lose(hand2);
            } else {
                System.out.println("打平...");
                player1.even(hand2);
                player2.even(hand1);
            }
        }

        System.out.println("最终结果:");
        System.out.println(player1);
        System.out.println(player2);
    }

}

程序输出:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Transformer简述和实现

Transformer 1、概述 (一)、诞生 自从2017年此文《Attention is All You Need》提出来Transformer后&#xff0c;便开启了大规模预训练的新时代&#xff0c;也在历史的长河中一举催生出了GPT、BERT这样的里程碑模型。 (二)、优势 相比之前占领市场的LSTM和GRU模型&#xf…

Astro 5.0 发布

Astro 5.0 发布&#xff01; 使用 Astro Content Layer 可以从任何来源加载内容&#xff0c;并使用 Server Islands 将缓存的静态内容与动态个性化内容结合起来。 什么是 Astro Astro 是用于构建内容驱动网站&#xff08;包括博客、营销和电子商务&#xff09;的 Web 框架。…

数据结构与算法-03链表-04

链表与递归 在链表操作中移除、反转经常会用到递归实现。通过力扣案例理解链表常规操作中的递归实现。 移除数据 删除链表的节点 问题 LCR 136. 删除链表的节点 - 力扣&#xff08;LeetCode&#xff09; 问题描述 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定…

Let up bring up a linux.part2 [十一]

之前的篇幅中我们已经将 Linux 内核 bringup 起来了&#xff0c;不知道大家有没有去尝试将根文件系统运行起来&#xff0c;今天我就带领大家完成这个事情&#xff0c;可以跟着下面的步骤一步步来完成&#xff1a; 在这里我们使用 busybox 构建 rootfs&#xff1a; 下载 busyb…

WEB开发: Node.js路由之由浅入深(一) - 全栈工程师入门

作为一个使用Node.js多年的开发者&#xff0c;我已经习惯于用Node.js写一些web应用来为工作服务&#xff0c;因为实现快速、部署简单、自定义强。今天我们一起来学习一个全栈工程师必备技能&#xff1a;web路由。&#xff08;观看此文的前提是默认你已经装好nonde.js了&#xf…

新书速览|循序渐进Node.js企业级开发实践

《循序渐进Node.js企业级开发实践》 1 本书内容 《循序渐进Node.js企业级开发实践》结合作者多年一线开发实践&#xff0c;系统地介绍了Node.js技术栈及其在企业级开发中的应用。全书共分5部分&#xff0c;第1部分基础知识&#xff08;第1&#xff5e;3章&#xff09;&#xf…

二代证信息读写器安卓身份证手持终端pda

HT530是一款可满足不同应用需求的多功能身份证核验手持机。Android 10操作系统&#xff0c;搭载高性能8核心2.0G主频处理器&#xff0c;5.5寸高清大屏&#xff0c;1300万摄像头&#xff1b;内存2G16G,4G64G可选。条码扫描&#xff08;扫描头可选&#xff09;、可离线采集、读取…

Redis的高可用之哨兵模式

Redis哨兵主要是解决Redis主从同步时主数据库宕机问题,使其能够自动进行故障恢复&#xff0c;提高Redis系统的高可用性。 1. 哨兵的作用&#xff1a; 监控&#xff1a;哨兵通过心跳机制监控主库和从库的存活性。 选主&#xff1a;当主库宕机时&#xff0c;哨兵会选举出一个领…

2024最新版python+pycharm安装与配置(mac和window都有讲)

PS&#xff1a;这篇是对于初学者的pythonpycharm配置教程 &#xff0c;配置完成后可以直接看我的python学习笔记来进行python全套学习目前正在持续更新。 目录 python以及pycharm的安装配置一、下载安装Python1、python环境检查2、系统环境检查3、python下载4、开始安装5、检查…

【css】基础(二)

本专栏内容为&#xff1a;前端专栏 记录学习前端&#xff0c;分为若干个子专栏&#xff0c;html js css vue等 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;css专栏 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &a…

OceanBase 的探索与实践

作者&#xff1a;来自 vivo 互联网数据库团队- Xu Shaohui 本文总结了目前我们遇到的痛点问题并通过 OceanBase 的技术方案解决了这些痛点问题&#xff0c;完整的描述了 OceanBase 的实施落地&#xff0c;通过迁移到 OceanBase 实践案例中遇到的问题与解决方案让大家能更好的了…

【开源免费】基于Vue和SpringBoot的服装生产管理系统(附论文)

博主说明&#xff1a;本文项目编号 T 066 &#xff0c;文末自助获取源码 \color{red}{T066&#xff0c;文末自助获取源码} T066&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

租赁小程序的优势与应用场景解析

内容概要 租赁小程序&#xff0c;听起来是不是很酷&#xff1f;其实&#xff0c;它就是一个让你可以方便地租借各种高成本但用得不频繁的商品的平台。想象一下&#xff0c;当你需要租一件派对用的华丽小礼服&#xff0c;或是想体验一下超酷的运动器材&#xff0c;租赁小程序就…

MySQL 权限管理分配详解

MySQL 权限管理分配详解 MySQL权限系统的工作原理权限表的存取用户通过权限认证、进行权限分配的流程账号管理我们常用的授权all privileges到底有哪些权限呢&#xff1f;以及带来的安全隐患有哪些&#xff1f;创建账户的时候最好分配指定的权限&#xff0c;这样子安全也高管理…

使用C#开发VTK笔记(一)-VTK开发环境搭建

一.使用C#开发VTK的背景 因为C#开发的友好性,一直都比较习惯于从C#开发程序。而长期以来,都希望有一个稳定可靠的三位工程数模的开发演示平台,经过多次对比之后,感觉VTK和OpenCasCade这两个开源项目是比较好的,但它们都是用C++编写的,我用C#形式开发,只能找到发布的C#组…

React 组件中 State 的定义、使用及正确更新方式

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容React 组件中 State 的定义、使用及正确更新方式 前言 在 React 应用开发中&#xff0c;state …

长沙市的科技查新机构有哪些

中南大学图书馆科技查新站&#xff1a; 中南大学图书馆科技查新站成立于2003年12月&#xff0c;中南大学图书馆科技查新站作为教育部首批批准的科技查新工作站之一&#xff0c;具备了在全国范围内开展科技查新工作的专业资质。 长沙理工大学科技查新工作站&#xff1a; 长沙理…

数组 - 八皇后 - 困难

************* C topic: 面试题 08.12. 八皇后 - 力扣&#xff08;LeetCode&#xff09; ************* Good morning, gays, Fridary angin and try the hard to celebrate. Inspect the topic: This topic I can understand it in a second. And I do rethink a movie, …

IDEA的service窗口中启动类是灰色且容易消失

大家在学习Spring Cloud的过程中,随着项目的深入,会分出很多个微服务,当我们的服务数量大于等于三个的时候,IDEA会给我们的服务整理起来,类似于这样 但是当我们的微服务数量达到5个以上的时候,再启动服务的时候,服务的启动类就会变成灰色,而且还容易丢失 解决方法 我们按住…