1

You have a video game in which upon killing final boss you get coins that get distributed based on whether you are:

  1. Person(individual)
  2. Group(consists of individuals or groups)

If reward for killing monster is 100. And the Fighters that killed monster together are:

  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 });

The output will be:

OUTPUT:

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.

with the below code:

Interface:

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);
    }
}

Group:

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();
            }
        }
    }

But, what if we were to give 10% extra weightage to those Person who belong to a group or a certain group? I could create class such as:

Female:IFighter, SeniorCitizen:IFighter to whom I want to give extra weightage.

How do you go about it?

3

I would not put any "setter" for Gold into IFighter, and only implement a setter for this property in the Person class, not in the Group class.

The amount of Gold for a group is the total sum of all the gold of all group members, that makes sense. But when gold is assigned to a group, how it is distributed specificially to each person is specific game logic (even if it is distributed equally, which requires a decision of whom to give the leftOver amount). Such specific game logic should IMHO not be a responsibility of the Composite itself.

One could, however, provide specific methods inside the Group class, like

  • DistributeGoldEvenlyAmongMembers(int gold), or

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

The benefit here is that is becomes ultimately clear that adding gold to a group is quite different operation than just adding the gold to a single person.

This could also be implemented somewhere outside, for example in a controller class which runs the aftermath of a fight. And yes, this will may require some type checkings if an object is a person or a group, but that is quite natural if the game logic requires it.

Alternatively, to make more generic operations possible, one could add a method

 IEnumerable<Person> GetAllPersons()

to the IFighter interface, which should deliver a flattened enumeration of all persons in the IFighter unit (the person itself, or the members of the group as well as each member of a subgroup). Such a method can be used to create, for example, some scoring logic which implements this "10% extra weightage" rule in a generic fashion which does not need to make a distinction between individuals and groups.

1

To get a different allocation than everybody the same portion, you can extend the IFighter interface with a Share property, which indicates (in a double) what portion of the gold should be given to that party member. A value of 1 should be the default and if everybody has a share of 1, then they all get an equal portion (give or take the left overs after rounding). If somebody has a share of 1.1, they would get approximately 10% extra.

The share of a group would typically not be the sum of the shares of its members, but the default 1. How the group sub-divides its portion of the gold is up to the group itself.

For example:

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();
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.