Mapmaker afwerken

Mapmaker afwerken

Vervolledig het all-in-project “Map Maker”. Inclusief interfaces en is/as .Kan je zelf extra zaken toevoegen zoals andere kamers, andere functionaliteit, etc.

Interface

We willen nu ervoor zorgen dat wanneer we volgende code schrijven, dat ook alle elementen van het Salon mee verhuizen naar de nieuwe locatie:

            List<MapObject> allObjects = new List<MapObject>();
            allObjects.Add(new SalonElement(new Point(5, 5)));
            allObjects[0].Paint();

          //Verplaats salon
            allObjects[0].Location= new Point(10,10);
            allObjects[0].Paint();

Echter, dat gebeurt niet. De oplossing is een gevorderd principe, maar eentje dat hopelijk het voordeel van een Interface laat zien.

We leggen een nieuwe interface IComposite vast die iedere composietklasse moet implementeren:

    interface IComposite
    {
        void UpdateElements(Point offset);
    }

Ons SalonElement wordt krijgt dan volgende aanpassing:

    class SalonElement: MapObject,IComposite
    {
        private List<MapObject> elementen= new List<MapObject>();

        public SalonElement(Point salonLoc)
        {
          ...
        }

        public override void Paint()
        {
        ...
        }

        public void UpdateElements(Point offset)
        {
          ...
        }
    }

De UpdateElements methode zou er dan als volgt kunnen uitzien:

            for (int i = 0; i < elementen.Count; i++)
            {
                Point elementLoc = elementen[i].Location;
                elementLoc.X += offset.X;
                elementLoc.Y += offset.Y;
                elementen[i].Location = elementLoc;
            }

Is/as

Telkens we dus UpdateElements aanroepen dan worden alle elementen die bij het object horen ook geüpdatet.

Nu rest ons nog één aanpassing, dat is ervoor zorgen dat deze methode ook effectief telkens wordt aangeroepen. De methode moet aangeroepen worden telkens we een aanpassing aan de Location van het SalonElement doen. Hierbij controleren we eerst of de locatie überhaupt al geïnitialiseerd is (anders is deze waarde gelijk aan ‘null’). Vervolgens berekenen we de offset, dit is het verschil tussen de huidige en de nieuwe locatie van de composietklasse. Daar Location bij MapObject hoort, moeten we dus in die klasse een aanpassing doen. We bereiden daarom de Locationproperty uit als volgt:

        public Point Location
        {
            get { return location; }
            set
            {
                Point prevloc = location;
                Point offset = new Point(1, 1);
                if (location != null)
                {

                    offset.X = value.X - prevloc.X;
                    offset.Y = value.Y - prevloc.Y;
                }

                location = value;
                if (this is IComposite)
                {
                    IComposite obj = this as IComposite;
                    obj.UpdateElements(offset);
                }

            }
        }

Deze code kan misschien wat toelichting gebruiken:

  • Telkens we de set aanroepen van Location (dus in bijvoorbeeld allObjects[0].Location= new Point(10,10);) dan veranderen sowieso de locatie van het object naar de nieuwe waarde.
  • We bewaren de huidige locatie zodat we de offset kunnen berekenen.
  • Indien er een ‘huidige locatie’ is (location!=null) dan berekenen we de offset in de x en de y richting.
  • Nu passen we de locatie van de composietklasse aan.
  • Vervolgens kijken we of het object in kwestie (aangegeven met this, daar we in het object zelf zitten) de IComposite interface ‘heeft’.
  • Als dit zo is dan zetten we het object even om naar een IComposite-object zodat we de UpdateElements()-methode kunnen aanroepen.

We kunnen nu dus zelfs een volledig Huis als klasse beschrijven en zo verschillende soorten huizen definiëren. Telkens we dan een huis verplaatsen dan verplaatst de hele inboedel mee.

Belangrijk: Het gebruik van de interface is hier louter illustratief. Dit probleem kan je beter oplossen door een CompositeElement klasse aan te maken die overerft van MapObject. Deze klasse bevat dan een lijst van elementen en een UpdateElements methode. In MapObject controleer je dan of een object van het type CompositeElement is (ipv IComposite)

Een nog betere oplossing is die waarbij je gewoon direct zegt dat MapObject een lijst van elementen kan bevatten. Als een MapObject exact 1 element in zijn lijst bevat dan is de werking dezelfde als ervoor, maar nu kunnen we dus zonder veel code aanpassingen ook composiet objecten aanmaken.

Think about it.