第三章-宝箱抽奖模块与代码设计(三)

第三章-宝箱抽奖模块与代码设计(三)

简要信息
作者卡卡
博客http://blog.csdn.net/kakashi8841
邮箱john.cha@qq.com
本文所属专栏http://blog.csdn.net/column/details/12687.html

上集回顾

由于本文章关联性较强,因此建议先阅读前一篇文章http://blog.csdn.net/kakashi8841/article/details/52374714

上集我们完成了代码后,虽然心中还想着优化代码,但是最终还是按捺不住诱惑想跑去约会了。
我:喂,是小丽吗?嗯,对,我们上次说好的今天吃饭。
小丽:啊?!啊。。。对。。可是我刚好有事,不好意思。要不下次?
我:。。。。
小丽:怎么啦?你怎么不说话啦?
我:没事,我。。。你刚刚说的是啥?我没听清楚。要不我们下次再聚吧?我突然想到怎么优化代码。
小丽:。。。
我:好嘛,就这次。88。
小丽:。。。

有时候我真佩服自己真**的敬业~才刚刚想到的三个问题居然瞬间来了解决的灵感。
哈哈。还吃啥呐。走,回去写代码去。^0^

三个问题是这样的:
1. 如果某些抽奖需要同时消耗两种资源怎么办
2. 感觉需求2的实现好像不是很通用,能不能把抽1次和抽10次的代码统一化,这样不需要if分支。
3. 10连抽的时候能不能不要刻意去区分前九次抽奖和第10次抽奖。

需求1实现

由于我们之前章节的处理,使得我们可以透明(“透明”指的是,策划可以随意配置消耗任意的资源类型和资源值,而程序不关心到底消耗的是什么)地处理资源。因此,想从消耗一种资源变成消耗两种。其实,可以进行这样的修改。

首先新增一个类,用于表示资源的类型和资源的值

package com.kakashi01.common;

public class KeyAndValue {

    private int key;
    private int value;

    public KeyAndValue(int key, int value) {
        super();
        this.key = key;
        this.value = value;
    }

    public int getKey() {
        return key;
    }

    public int getValue() {
        return value;
    }
}

修改ConfigLottery

去掉之前的cost和costType字段,增加KeyValue类型的数组字段costs。修改后的ConfigLottery代码如下

package com.kakashi01.lottery.domain;

import java.util.List;

import com.kakashi01.common.KeyAndValue;

public class ConfigLottery {

    public static final int         SLIVER  = 1;
    public static final int         GOLD    = 2;
    public static final int         DIAMOND = 3;

    private int                     lotteryType;
    private int                     timesType;
    private KeyAndValue[]           costs;                              // 抽奖需要消耗的资源

    private List<ConfigLotteryItem> items;                              // 掉落的物品

    public int getLotteryType() {
        return lotteryType;
    }

    public void setLotteryType(int lotteryType) {
        this.lotteryType = lotteryType;
    }

    public int getTimesType() {
        return timesType;
    }

    public void setTimesType(int timesType) {
        this.timesType = timesType;
    }

    public List<ConfigLotteryItem> getItems() {
        return items;
    }

    public void setItems(List<ConfigLotteryItem> items) {
        this.items = items;
    }

    public KeyAndValue[] getCosts() {
        return costs;
    }

    public void setCosts(KeyAndValue[] costs) {
        this.costs = costs;
    }
}

LotteryService

修改tryCostResource方法,改为扣除ConfigLottery中costs数组的指定的资源。

private boolean tryCostResource(Player player, ConfigLottery configLottery) {
  return player.costResources(configLottery.getCosts());
}

而调用player的costResources方法代码如下:

修改Player

Player中增加如下方法


public boolean costResources(KeyAndValue[] costs) {
  if (!isEnough(costs)) {
    return false;
  }
  for (KeyAndValue keyAndValue : costs) {
    alterResource(keyAndValue.getKey(), keyAndValue.getValue());
  }
  return true;
}

public boolean isEnough(KeyAndValue[] keyAndValues) {
  for (KeyAndValue keyAndValue : keyAndValues) {
    if (getResource(keyAndValue.getKey()) < keyAndValue.getValue()) {
      return false;
    }
  }
  return true;
}

至于LotteryDemo中报错的代码,就留给你修改了。如果你懒得修改了,也可以切换代码分支到step-2-0来获得目前的完整代码。
至此,我们完成了第1个需求的实现。

需求2、3实现

先回顾下我们现在抽奖的方法,看LotteryService中的dropItem方法,如下:

private void dropItem(ConfigLottery configLottery) {
  Random rand = new Random();
  if (configLottery.getTimesType() == 1) {
    dropOnce(rand, configLottery);
  } else if (configLottery.getTimesType() > 1) {
    for (int i = 0; i < configLottery.getTimesType() - 1; i++) {
      dropOnce(rand, configLottery);
    }
    dropOnce(rand, getConfigLottery(configLottery.getLotteryType(), 0));
  }
}

看代码可以发现,我们之所以判断timesType是否为1,是因为,如果为1,则采用策划配置的数据掉落1次。如果不为1,则前N-1次采用策划配置的数据进行掉落。而第N次则采用策划配置的另一条特殊数据,这条数据的timesType为0。
举个直观的例子:
假如策划想要配置了1个黄金宝箱(lotteryType为2)的10连抽,而且要求是前9次从一个转盘随机掉物品,第10次则从另一个更高级的转盘(比如这个转盘全部是紫色的物品)掉物品。那么目前策划需要配置两个ConfigLottery数据。分别如下:

lotteryTypetimesTypecostsitems
2101:1000,2:1100:2,101:2,103:3
201:1000,2:1200:2,201:2,203:3

也就是我们引用了timesType为0这个特殊情况来处理策划的10连抽必出xxx的需求。
看起来好像是个“小聪明”,至少抽奖的掉落代码我们是重用。但是,还有没有更优雅的方式来实现这个需求呢。答案是肯定的。我们继续看。
如果我们能把10连抽每次抽奖得到的东西交给策划来配置,那么不就可以使用代码统一处理1次和10次抽奖吗?
因此,我想了下面的处理方法。一个ConfigLottery代表的是一种抽奖。比如黄金宝箱抽1次和黄金宝箱10连抽,这就是两种抽奖。而我代码其实根本不需要关心他们到底是几连抽。我们只要扣除抽奖所需要的资源,然后抽奖。扣除抽奖所需要的资源上面的需求1的实现已经支持策划配置消耗任意资源。那么,接下来要做的就是,支持策划配置一个抽奖到底能抽几次东西,每次出什么。

增加一个抽奖物品库的类ConfigLotteryItemLib

package com.kakashi01.lottery.domain;

import java.util.List;
import java.util.Random;

public class ConfigLotteryItemLib {

    private int                     modelID;
    private List<ConfigLotteryItem> items;                                                                      // 掉落的物品
    private int                     totalWeight;

    public ConfigLotteryItemLib(int modelID, List<ConfigLotteryItem> items) {
        super();
        this.modelID = modelID;
        this.items = items;
        for (ConfigLotteryItem item : items) {
            totalWeight += item.getWeight();
        }
    }

    public int getModelID() {
        return modelID;
    }

    public ConfigLotteryItem randomItem(Random rand) {
        int randNum = rand.nextInt(totalWeight);
        for (ConfigLotteryItem item : items) {
            if (randNum < item.getWeight()) {
                System.out.println("Drop item " + item.getModelID() + ", " + item.getNum());
                return item;
            }
            randNum -= item.getWeight();
        }
        return null;
    }

}

这个类用于表示一个随机物品库,调用randomItem则可以产生一个随机物品。
可以看到这个类其实的大部分代码都比较熟悉了。构造函数中的for循环是之前LotteryService中dropOnce的第一个循环的内容。用于计算总的权重,在构造函数进行计算,避免了每次调用randomItem都计算。randomItem的方法则是之前LotteryService中dropOnce的第二个循环中的代码稍微修改而来。那么这个类应该怎么用呢。这个类主要为了将ConfigLottery中的items字段的功能进行增强。之前一条ConfigLottery调用一次代表的是一次掉落。所以掉落10次只能通过timesType来识别。然后进行循环,最后导致策划需要在第十次掉落不同东西的时候,我们代码还额外处理了一下。
现在有了这个类,我们修改ConfigLottery中的代码。

修改ConfigLottery

修改之前的items字段的类型,由

List<ConfigLotteryItem> items

改为:

int[] configLotteryItemLibModelIDs  

这样修改是什么意思?
之前的List代表的是一个转盘中的所有物品。而现在的ConfigLotteryItemLib其实已经是对应一个转盘了。那么为什么要改成int[] configLotteryItemLibModelIDs,如果你再想想,或许你也可以猜测到我这么改的意图,也就是,刚刚说的,我想把每一条ConfigLottery应该怎么抽的这个控制权完全交给策划。
比如现在有两个转盘ConfigLotteryItemLib的数据为:

modelIDitems
1100:2,101:2,103:3
2200:2,201:2,203:3

利用这个数据,我们可以配置黄金宝箱10连抽的数据为:

lotteryTypetimesTypecostsconfigLotteryItemLibModelIDs
2101:1000,2:11,1,1,1,1,1,1,1,1,2

没错,我们只要根据items中配置的转盘ID数组来取得具体的转盘配置,再进行该转盘的掉落就OK了。上面的配置就表示前9次都掉落转盘1,第10次掉了转盘2。如果想配置黄金宝箱抽一次的配置,只要像下面的一样:

lotteryTypetimesTypecostsconfigLotteryItemLibModelIDs
211:1201

可以发现配置掉1次和掉10次其实是一样的,只是消耗的资源(cost)不同,抽多少次以及从哪个物品库抽不同(由configLotteryItemLibModelIDs字段决定)。
既然配置是统一的,那么代码应该也是统一的。

修改LotteryService

修改dropItem方法

private void dropItem(ConfigLottery configLottery) {
    Random rand = new Random();
    int[] configLotteryItemLibModelIDs = configLottery.getConfigLotteryItemLibModelIDs();
    for (int configLotteryItemLibModelID : configLotteryItemLibModelIDs) {
        ConfigLotteryItemLib configLotteryItemLib = getConfigLotteryItemLib(configLotteryItemLibModelID);
        configLotteryItemLib.getRandomItem(rand);
    }
}

可以看到,已经去掉了抽取次数的判断,也没有去取timesType为0的特殊数据作为第十次抽奖的配置。代码的处理更加的统一。LotteryService中还需要增加的代码有:

private final Map<Integer, ConfigLotteryItemLib>        lotteryItemLibMap   = new HashMap<>();

public void addConfigLotteryItemLib(ConfigLotteryItemLib configLotteryItemLib) {
    lotteryItemLibMap.put(configLotteryItemLib.getModelID(), configLotteryItemLib);
}

private ConfigLotteryItemLib getConfigLotteryItemLib(int modelID) {
    return lotteryItemLibMap.get(modelID);
}

这几行代码比较简单,就不赘述了。
再修改LotteryDemo中配置数据的代码就OK啦。

LotteryDemo中的修改这里就不列出来了。我已经修改并提交到git中。大家可以在git中切换到step-2-1来获取本章最终代码。

最终代码分支

  1. 项目git地址 https://github.com/johncha/CodeDesign-1
  2. step-2-0 分支中获得本章最终代码。

如果你对本文有什么建议或意见,可以发邮件到john.cha@qq.com或到blog.csdn.net/kakashi8841中留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值