web-dev-qa-db-ja.com

重みが等しくない複合デザインパターン

あなたは、最後のボスを倒すと、あなたが次のいずれであるかに基づいて分配されるコインを得るビデオゲームを持っています:

  1. 人(個人)
  2. グループ(個人またはグループで構成)

モンスターを殺した報酬が100の場合。モンスターを一緒に殺したファイターは次のとおりです。

  var joe = new Person("Joe");//Person
  var jake = new Person("jake");//Person
  var emily = new Person("emily");//Person

  var oldBob = new Person("oldBob");//Person that belongs to a group
var newBob = new Person("newBob");//Person that belongs to a group
      var familyGroup = new Group("familyGroup", new List<IFighter>() { newBob, oldBob });

出力は次のようになります。

出力:

You have killed the Giant IE6 Monster and gained 100 gold!
MegaParty has 100 gold coins.
familyGroup has 25 gold coins.
--newBob has 13 gold coins.
--oldBob has 12 gold coins.
--Joe has 25 gold coins.
--jake has 25 gold coins.
--emily has 25 gold coins.

以下のコードで:

インターフェース:

public interface IFighter
{
    string Name { get; }
    int Gold { get; set; }      
    void Stats();
}
**CLIENT:**

class Program
{
    static void Main(string[] args)
    {
        int goldForKill = 100;
        Console.WriteLine("You have killed the Giant IE6 Monster and gained {0} gold!", goldForKill);
        var joe = new Person("Joe");
        var jake = new Person("jake");
        var emily = new Person("emily");
        var oldBob = new Person("oldBob");
        var newBob = new Person("newBob");
        var familyGroup = new Group("familyGroup", new List<IFighter>() { newBob, oldBob });
        var parties = new Group("MegaParty", new List<IFighter> { familyGroup, joe, jake, emily });
        parties.Gold += goldForKill;
        parties.Stats();
        Console.ReadKey();
    }

Person:

public class Person : IFighter
{
    public string Name { get; }
    public int Gold { get; set; }

    public Person(string name)
    {
        this.Name = name;            
    }

    public void Stats()
    {
        Console.WriteLine("--{0} has {1} gold coins.", Name, Gold);
    }
}

グループ:

public class Group : IFighter
{
    public string Name { get; set; }
    public List<IFighter> Members { get; set; }
    public int Gold
    {
         get
         {
             int totalGold=0;
            foreach (var member in Members)
            {
                totalGold += member.Gold;
            }
            return totalGold;
        }
        set
         {
            var eachSplit = value /this.Members.Count;;
            var leftOver = value % this.Members.Count;;
            foreach (var member in Members)
            {
                member.Gold = eachSplit +leftOver;
                leftOver = 0;
            }
         }
        }

        public Group(string name, List<IFighter> members)
        {
            Name = name;
            Members = members;
        }    

        public void Stats()
        {
            Console.WriteLine("{0} has {1} gold coins.", this.Name, Gold);
            foreach (var member in Members)
            {
                member.Stats();
            }
        }
    }

しかし、グループまたは特定のグループに属する人に10%の追加の重みを与えるとしたらどうなるでしょうか?次のようなクラスを作成できます。

Female:IFighter, SeniorCitizen:IFighter追加の重みを付けたい人。

どうやってそれをどうしますか?

1
SamuraiJack

Goldの「セッター」をIFighterに入れず、Personクラスではなく、Groupクラスでこのプロパティのセッターのみを実装します。

グループのGoldの金額は、すべてのグループメンバーのすべてのゴールドの合計であり、これは理にかなっています。しかし、金がグループに割り当てられた場合、金が各個人に具体的に分配される方法は特定のゲームロジックです(金が均等に分配される場合でも、leftOverの金額を誰に与えるかを決定する必要があります)。そのような特定のゲームロジックは、Composite自体の責任ではありません。

ただし、次のようなGroupクラス内に特定のメソッドを提供できます。

  • DistributeGoldEvenlyAmongMembers(int gold)、または

  • DistributeGoldWeightedAmongMembers(int gold, /* maybe extra parameters here*/)

ここでの利点は、グループに金を追加することは、単一の人に金を追加することとはまったく異なる操作であることが最終的に明らかになることです。

これは、戦闘の余波を実行するコントローラクラスなど、外部のどこかに実装することもできます。そして、はい、これはオブジェクトが人またはグループである場合、いくつかの型チェックを必要とする可能性がありますが、ゲームロジックがそれを必要とする場合、それは非常に自然なことです。

あるいは、より一般的な操作を可能にするために、メソッドを追加できます

 IEnumerable<Person> GetAllPersons()

IFighterインターフェースのIFighterユニット内のすべての個人(個人自体、またはグループのメンバーとサブグループの各メンバー)のフラット化された列挙を提供する必要があります。このようなメソッドは、たとえば、この「10%の追加の重み付け」ルールを一般的な方法で実装するスコアリングロジックを作成するために使用でき、個人とグループを区別する必要はありません。

3
Doc Brown

誰もが同じ部分とは異なる割り当てを取得するには、IFighterインターフェースをShareプロパティで拡張できます。メンバー。値1がデフォルトである必要があり、全員のシェアが1である場合、それらはすべて等しい部分を取得します(四捨五入後に余りを与えるか取ります)。誰かが1.1のシェアを持っている場合、彼らは約10%余分に得ます。

グループのシェアは通常notがそのメンバーのシェアの合計になりますが、デフォルトは1です。グループがゴールドのその部分をどのように細分するかは、グループ自体次第です。

例えば:

public interface IFighter
{
    string Name { get; }
    double Share { get; }
    int Gold { get; set; }      
    void Stats();
}

public class Person : IFighter
{
    public string Name { get; }
    public double Share { get; }
    public int Gold { get; set; }

    public Person(string name, double share = 1.0)
    {
        this.Name = name;            
        this.Share = share;
    }

    public void Stats()
    {
        Console.WriteLine("--{0} has {1} gold coins.", Name, Gold);
    }
}

public class Group : IFighter
{
    public string Name { get; set; }
    public double Share { get; }
    public List<IFighter> Members { get; set; }

    public Group(string name, List<IFighter> members)
    {
        Name = name;
        Members = members;
        this.Share = 1;
    }    

    public int Gold
    {
      get
      {
        int totalGold=0;
        foreach (var member in Members)
        {
            totalGold += member.Gold;
        }
        return totalGold;
      }
      set
      {
        double sumShares = 0;
        foreach (var member in Members)
        {
          sumShares += member.Share;
        }
        var eachSplit = value / sumShares;
        var leftOver = value;
        foreach (var member in Members)
        {
            int memberShare = round(eachSplit * member.Share);
            member.Gold = memberShare;
            leftOver -= memberShare;
        }
        // Allocating the leftover amount left as an exercise for the reader ;-)
      }
    }

    public void Stats()
    {
        Console.WriteLine("{0} has {1} gold coins.", this.Name, Gold);
        foreach (var member in Members)
        {
            member.Stats();
        }
    }
}