`
xieye
  • 浏览: 842369 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

php设计模式-策略模式-例题学习

    博客分类:
  • PHP
阅读更多
本文改自《设计模式-java语言中的应用》中的策略模式章节。作者:结城 浩(日本)

简单的说,策略模式就是算法替换,用不同的类实现不同的算法。
难点:需要根据算法设计出不同的方法,参数等。

程序示例
这里的程序是计算机游戏“剪刀石头布”。
猜拳时的策略有两种方法。第一种方法有点笨,“猜赢之后继续出同样的招式”(WinningStrategy),第2种方法则是“从上一次出的招式,以概率分配方式求出下一个招式的几率”(ProbStrategy)。

类一览表。
Hand:表示猜拳手势的类
HandStrategyInterface:表示猜拳战略的接口
Player:表示玩猜拳的游戏者的类
WinningStratrgy:策略1,表示猜赢之后继续出同样招式的战略的类
ProbStratrgy:策略2,表示根据上一次出的招式以概率计算出下一个招式的类。
Main:执行主类

代码在附件。

Hand.php
<?php
/**
 * 手势类
 *
 * @author 结城 浩
 */
class Hand
{
    public static $HANDVALUE_GUU = 0; //表示石头的值
    public static $HANDVALUE_CHO = 1; //表示剪刀的值
    public static $HANDVALUE_PAA = 2; //表示布的值

    private static $name_arr = array('石头', '剪刀', '布');

    /**
     * 手势的值
     *
     * @var int
     */
    private $handValue;

    /**
     * 私有构造方法
     *
     * @param int $handvalue 手势的值
     */
    private function __construct($handvalue)
    {
        $this->handValue = $handvalue;
    }

    /**
     * 该方法实现单例数组,里面存放3个对象
     *
     * @return array 对象数组
     */
    public static function getHandArr()
    {
        static $hand_arr = array();
        if (!$hand_arr) {
            $hand_arr = array(
            new Hand(self::$HANDVALUE_GUU),
            new Hand(self::$HANDVALUE_CHO),
            new Hand(self::$HANDVALUE_PAA),
            );
        }
        return $hand_arr;
    }

    /**
     * 外部调用,获取一个手势对象
     *
     * @param int $handvalue 手势的值
     * @return Hand 一个手势对象
     */
    public static function getHand($handvalue)
    {
        $hand = self::getHandArr();
        return $hand[$handvalue];
    }

    /**
     * 判断是否战胜另一个手势
     *
     * @param Hand $h 另一个手势对象
     * @param boolean
     */
    public function isStrongerThan(Hand $h)
    {
        return $this->fight($h) == 1;
    }

    /**
     * 判断是否输给另一个手势
     *
     * @param Hand $h 另一个手势对象
     * @param boolean
     */
    public function isWeakerThan(Hand $h)
    {
        return $this->fight($n) == -1;
    }

    /**
     * 与另一个手势判断输赢
     *
     * @param Hand $h 另一个手势对象
     * @return int 胜为1 ,负为-1,平局为0
     */
    private function fight(Hand $h)
    {
        if ($this->handValue == $h->handValue) {
            return 0;
        } elseif (($this->handValue + 1) % 3 == $h->handValue) {
            return 1;
        } else {
            return -1;
        }
    }

    /**
     * 字符串显示
     */
    function __toString()
    {
        return self::$name_arr[$this->handValue];
    }

}



HandStrategyInterface.php
<?php
/**
 * 手势策略接口
 */
interface HandStrategyInterface
{
    /**
     * 得到下一个手势
     */
    public function nextHand();
    
    /**
     * 策略的智能学习
     */
    public function study($win);
}


Player.php
<?php


require_once 'HandStrategyInterface.php';

/**
 * 游戏者类
 *
 * @author 结城 浩
 */
class Player
{
    /**
     * 游戏者名称
     * 
     * @var string
     */
    private $name;
    
    /**
     * 私有策略对象
     * 
     * @var HandStrategyInterface
     */
    private $strategy;
    
    /**
     * 总胜利次数
     * 
     * @var int
     */
    private $wincount;
    
    /**
     * 总失败次数
     * 
     * @var int
     */
    private $losecount;
    
    /**
     * 总游戏次数
     * 
     * @var int
     */
    private $gamecount;

    /**
     * 构造方法
     * 
     * @param string $name 游戏者名称
     * @param HandStrategyInterface $strategy 策略对象
     */
    function __construct($name, HandStrategyInterface $strategy)
    {
        $this->name = $name;
        $this->strategy = $strategy;
        $this->wincount = $this->losecount = $this->gamecount = 0;
    }

    /**
     * 根据策略返回游戏者的下一个手势
     * 
     * @return Hand 游戏者的下一个手势
     */
    public function nextHand()
    {
        $hand = $this->strategy->nextHand();
        echo $this->name . ":" . $hand ."<br>";
        return $hand;
    }
    
    /**
     * 胜利后的处理
     */
    public function win()
    {
        $this->strategy->study(true);
        $this->wincount++;
        $this->gamecount++;
    }

    /**
     * 失败后的处理
     */
    public function lose()
    {
        $this->strategy->study(false);
        $this->losecount++;
        $this->gamecount++;
    }

    /**
     * 平局后的处理
     */
    public function even()
    {
        $this->gamecount++;
    }

    /**
     * 魔术方法显示游戏者信息
     * 
     * @return string
     */
    function __toString()
    {
        return '[' . $this->name . ':' . $this->gamecount . ' games, ' .
        $this->wincount . ' win, ' . $this->losecount . ' lose]';
    }
}


WinningStrategy.php
<?php

require_once 'Hand.php';
require_once 'HandStrategyInterface.php';

/**
 * 简单策略类
 *
 * 这是第一个策略,非常简单,如果赢了,就继续同一个手势,如果输了,随机出手势。
 *
 * @author 结城 浩
 */
class WinningStrategy implements HandStrategyInterface
{
    /**
     * 与该策略算法有关的变量
     * 
     * @var boolean
     */
    private $won = false;
    
    /**
     * 上一个手势
     * 
     * @var Hand
     */
    private $prevhand;
    
    
    public function __construct()
    {
    }

    /**
     * 策略的算法核心。
     * 
     * 如果赢,就继续相同的手势,输则随机产生一个。
     * 
     * @return Hand
     */
    public function nextHand()
    {
        if (!$this->won) {
            $this->prevhand = Hand::getHand(mt_rand(0, 2));
        }
        
        return $this->prevhand;
    }

    /**
     * 学习方法,只记住当前的输赢结果
     * 
     * @param boolean 输赢结果
     */
    public function study($win)
    {
        $this->won = $win;
    }

}


ProbStrategy.php
<?php

require_once 'Hand.php';
require_once 'HandStrategyInterface.php';

/**
 * 复杂策略类
 *
 * 这是第二个策略,虽然下一次的手势都是由随机数决定,不过它会参考之前的输赢记录,机动性的更改手势的出现几率。
 * history字段是一份列出过去输赢记录的表格,用来计算几率。history是一个int类型的2维数组,各下标的含义是
 * history[上一次的手势][下一次的手势]
 * 这个表达式的值越大,则代表之前获胜的几率越高。用实际的数据可能会更清楚一点。
 * 这个作战策略的前提是对手的出拳方式也有另一种Pattern。
 *
 * @author 结城 浩
 */
class ProbStrategy implements HandStrategyInterface
{
    /**
     * 前面手势的值,统计用
     * 
     * @var int
     */
    private $prevHandValue = 0;
    
    /**
     * 后面手势的值,统计用
     * 
     * @var int
     */
    private $currentHandValue = 0;
    
    /**
     * 存放历史数据的数组
     * 
     * history[0][0] 石头之后出石头的不败(获胜或平手)次数。
     * history[0][1] 石头之后出剪刀的不败(获胜或平手)次数。
     * history[0][2] 石头之后出布的不败(获胜或平手)次数。
     * history[1][0] 剪刀之后出石头的不败(获胜或平手)次数。
     * history[1][1] 剪刀之后出剪刀的不败(获胜或平手)次数。
     * history[1][2] 剪刀之后出布的不败(获胜或平手)次数。
     * history[2][0] 布之后出石头的不败(获胜或平手)次数。
     * history[2][1] 布之后出剪刀的不败(获胜或平手)次数。
     * history[2][2] 布之后出布的不败(获胜或平手)次数。
     * 
     * @var array 二维数组
     */
    private $history = array(
        array(1, 1, 1),
        array(1, 1, 1),
        array(1, 1, 1),
    );

    public function __construct()
    {

    }

    /**
     * 算法核心
     * 
     * 根据历史数据,计算出某一个手势的下一个手势的3个概率(比如6:4:13),然后
     * 也不一定就是那个最大概率的手势,而是依然根据概率得到下一个手势。
     */
    public function nextHand()
    {
        $bet = mt_rand(0, $this->getSum($this->currentHandValue) - 1);
        $handvalue = 0;
        if ($bet < $this->history[$this->currentHandValue][0]) {
            $handvalue = 0;
        } elseif ($bet < ($this->history[$this->currentHandValue][0] +
        $this->history[$this->currentHandValue][1]) ) {
            $handvalue = 1;
        } else {
            $handvalue = 2;
        }
        $this->prevHandValue = $this->currentHandValue;
        $this->currentHandValue = $handvalue;
        $hand = Hand::getHand($handvalue);
        return $hand;
    }

    /**
     * 从历史数据中得到某一个手势的次数之和,算概率用的
     * 
     * @param int $hv
     * @return int 
     */
    private function getSum($hv)
    {
        $sum = 0;
        for ($i = 0; $i < 3; $i++) {
            $sum += $this->history[$hv][$i];
        }
        return $sum;
    }

    /**
     * 该策略的学习就是把结果记入统计数据里。
     * 
     * @param boolean $win 本次的输赢结果
     */
    public function study($win)
    {
        if ($win) {
            $this->history[$this->prevHandValue][$this->currentHandValue]++;
        } else {
            $this->history[$this->prevHandValue][($this->currentHandValue + 1) % 3]++;
            $this->history[$this->prevHandValue][($this->currentHandValue + 2) % 3]++;
        }
    }
}


Main.php
<?php

require_once 'Hand.php';
require_once 'WinningStrategy.php';
require_once 'ProbStrategy.php';
require_once 'Player.php';


/**
 * 执行主类
 * 
 * 使用方法,把这几个类一起拷贝到web服务器的docment_root路径下,然后
 * http://localhost/Main.php
 *
 * @author 结城 浩
 */
class Main
{
    public static function run()
    {
        //得到两个游戏者,策略不同
        $player1 = new Player('张三', new WinningStrategy());
        $player2 = new Player('李四', new ProbStrategy());

        //1000次运算,得到结果,并不停的输出。
        for ($i = 0; $i < 1000; $i++) {
            $nextHand1 = $player1->nextHand();
            $nextHand2 = $player2->nextHand();
            if ($nextHand1->isStrongerThan($nextHand2)) {
                $player1->win();
                $player2->lose();
                self::echoinfo('胜利者: ' . $player1);
            } elseif ($nextHand2->isStrongerThan($nextHand1)) {
                $player1->lose();
                $player2->win();
                self::echoinfo('胜利者: ' . $player2);
            } else {
                $player1->even();
                $player2->even();
                self::echoinfo('平手。 ');
            }
            self::echoinfo(' ');
        }
        //统计
        self::echoinfo('======================= 低调滴分割线  =======================');
        self::echoinfo($player1);
        self::echoinfo($player2);
    }

    /**
     * 只是一个便利的输出换行
     * 
     * @param  string $s 待输出信息
     * @return string 加了换行后的输出
     */
    public static function echoinfo($s)
    {
        echo $s . "<br />\n";
    }
}

//执行
Main::run();


运行结果示例:


代码在附件。
  • 大小: 21.4 KB
分享到:
评论

相关推荐

    设计模式-简单工厂模式-例题

    ### 设计模式——简单工厂模式解析与应用实例 #### 一、简单工厂模式概述 简单工厂模式(Simple Factory Pattern)是一种常用的创建型设计模式。它提供了一种方式来封装对象的创建过程,使得整个系统更加灵活,...

    设计模式--装饰者模式java例子

    装饰者模式是软件设计模式中的一种结构型模式,它的主要目的是动态地给对象添加新的功能,而无需修改原有代码。在Java中,装饰者模式通常通过继承和组合来实现,它提供了一种比继承更灵活的方式来扩展对象的功能。...

    Java 工厂设计模式例题

    例题简单的阐述了Java工厂模式的概念,有利于读者更好地了解Java工厂模式

    C#,设计模式,策略模式,鸭子,

    C#是一种广泛应用的面向对象的编程语言,它支持多种设计模式,其中包括策略模式。策略模式是一种行为设计模式,允许我们在运行时根据需要改变一个对象的行为。 策略模式的核心思想是定义一系列的算法,并将每一个...

    设计模式例题 C#版

    本资料包"设计模式例题 C#版"包含了C#实现的设计模式示例代码,所有代码均经过调试,确保功能正确,易于理解。 一、单例模式(Singleton) 单例模式确保一个类只有一个实例,并提供全局访问点。在C#中,可以通过...

    《Java设计模式》刘伟 课后习题及模拟试题答案.rar

    《Java设计模式》是刘伟老师撰写的经典教材,它为Java程序员深入理解和应用设计模式提供了极佳的学习资源。设计模式作为软件工程中解决特定问题的典型方案,是每一个有志于成为高级开发者的程序员不可或缺的知识储备...

    C++程序设计-清华大学-郑莉-例题源代码

    《C++程序设计-清华大学-郑莉-例题源代码》是针对郑莉教授主编的《C++程序设计》一书中的例题所编写的源代码集合。这本书是中国计算机科学教育领域的重要教材,旨在帮助学生深入理解C++编程语言,掌握程序设计的...

    E--R图转换成关系模式规则与例题

    《E-R图转换成关系模式规则与例题》 在数据库设计中,E-R图(实体-关系图)是一种常用的概念建模工具,用于描述实体、属性和实体之间的关系。将其转换为关系模式是数据库实现的关键步骤。以下是E-R图转换成关系模式...

    常用设计模式例题(原创)

    在给定的压缩包文件中,我们可以看到涉及到九种基本的设计模式,它们分别是:组合模式(Composite)、策略模式(Strategy)、外观模式(Facade)、观察者模式(Observer)以及单例模式(Singleton)。接下来,我们将...

    相关例题---GPSS

    7. **例题解析**:"例题-GPSS"可能包含一系列实际问题的解决方案,例如,模拟一个简单的生产线,研究其瓶颈、优化调度策略,或者模拟交通流量,分析交通拥堵情况等。通过这些例题,学习者可以深入理解GPSS的应用和...

    98-例题-例题答案1

    这里通过分析提供的例题,我们可以深入探讨几个关键的知识点: 1. **信号量机制**:信号量是一种用于实现进程间同步和互斥的工具。题目中提到的信号量S,其值的含义是: - 当S.value&gt;0时,表示有S个资源可用。 - ...

    156-例题-IO系统例题1

    以“156-例题-IO系统例题1”为例,我们将深入探讨磁盘块空闲状态管理,磁盘的旋转速度,以及磁盘替换为Flash半导体存储器的相关问题。 首先,我们来说明如何进行磁盘块空闲状态的管理。磁盘块空闲状态管理是文件...

    MATLAB程序设计与应用-刘卫国-例题源程序(DOC)

    压缩包中的"MATLAB程序设计与应用-刘卫国-例题源程序(DOC)"文档,很可能是包含了书中所有例题的MATLAB代码,这为读者提供了亲自动手实践的机会,是学习和提高MATLAB技能的重要资源。读者可以通过运行和修改这些源...

    “C语言程序设计”--例题源代码.zip

    这个压缩包“C语言程序设计”--例题源代码.zip包含了丰富的教学资料,特别是各种例题的源代码,是学习和理解C语言语法、逻辑控制以及算法设计的宝贵资源。 1. **C语言基础** C语言是一种中级语言,它的语法简洁...

    数据库原理---例题.doc

    数据库原理---例题

    吉林大学软件设计模式PPT例题

    《吉林大学软件设计模式PPT例题》是一个深入学习和实践设计模式的宝贵资源,它包含了一系列可直接在IDEA(IntelliJ IDEA)中运行的例题。设计模式是软件工程中的重要概念,它是一种在特定情境下解决常见问题的最佳...

    VHDL程序设计-教程-例题-复习概要

    一、VHDL程序设计约定 语句结构描述中方括号“[ ]”内的内容为可选内容。 对于VHDL的编译器和综合器来说,程序文字的大小写是不加区分的。 程序中的注释使用双横线“- -”。 源程序命名与实体同名(MAX+plus Ⅱ...

    面向对象程序设计(Java)第十章例题代码-J7chapte10

    面向对象程序设计(Java)第十章例题代码-J7chapte10面向对象程序设计(Java)第十章例题代码-J7chapte10面向对象程序设计(Java)第十章例题代码-J7chapte10面向对象程序设计(Java)第十章例题代码-J7chapte10面向...

    设计模式一(单子、工厂、策略模式)练习 .zip

    设计模式一(单子、工厂、策略模式)练习 1. 假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。对所有的教材类图书 实行每本一元的折扣;对连环画类图书提供每本 7%的促销折扣;而对非教材类的计算 机...

Global site tag (gtag.js) - Google Analytics