Cvičenie č. 3 - Úvod do OOP

Dôležité pojmy

  • trieda vs objekt vs inštancia (class vs object vs instance)
  • konštruktor (constructor)
  • preťažovanie metód a konštruktorov
  • statické triedy a metódy
  • virtuálne a abstraktné metódy
  • 3 základné princípy OOP:
    • zapúzdrenie (encapsulation)
    • dedičnosť (inheritance)
    • polymorfizmus (polymorphism)

Príklad č. 1: Objektová reprezentácia 2D geometrických útvarov

Navrhnite a naprogramujte reprezentáciu 2D geometrických útvarov: trojuhoľník, štvorec, obdĺžník a kruh. Každý útvar musí vedieť:

  • spočítať svoj obsah
  • uchovať si svoju farbu reprezentovanú výčtovým typom (enum)
  • vedieť vypísať svoju farbu ako reťazec

Na záver napíšte metódu, ktorá dostane na vstup zoznam inštancií (napr. kruh, kruh, štvorec…) týchto útvarov a vráti súčet ich obsahov.

Riešenie:

namespace Class3
{
  // Vyctovy typ reprezentujuci farbu.
  enum Color {
    BLACK, RED, BLUE, WHITE
  }

  // Spolocny interface pre vsetky 2D objekty.
  interface Object2D {

    // Vrati obsah 2D objektu.
    double calculateArea();

    // Vrati farbu objektu ako string.
    string getColorAsString();

  }

  // Spolocna abstraktna trieda, ktora implementuje interface Object2D.
  // V tejto triede naimplementujeme ukladanie a vypisovanie farby objektu,
  // ktore bude rovnake pre vsetky 2D objekty.
  abstract class Object2DBase : Object2D
  {
    // Premenna pre ulozenie farby objektu.
    // Miesto privlastku private pouzijeme protected, aby bola tato premenna
    // pristupna aj z objektov, ktore budu dedit od Object2DBase.
    protected Color color = Color.BLACK;

    // Implementaciu metody calculateArea este nebudeme implementovat a
    // preto ju oznacime klucovym slovom abstract a nechame
    // ju bez implementacie.
    public abstract double calculateArea();

    // Naimplementujeme metodu getColorAsString interfacu Object2D.
    public string getColorAsString()
    {
      switch (this.color)
      {
        case Color.BLUE:
          return "Modra";
        case Color.WHITE:
          return "Biela";
        case Color.RED:
          return "Cervena";
        case Color.BLACK:
          return "Cierna";
        default:
          return "Neznama farba";
      }
    }
  }

  // Implementacia kruhu podla interfacu Object2D.
  // Trieda Circle dedi priamo od Object2DBase a tranzitivne aj
  // interface Object2D. Takto Circle zdedi vsetok kod, ktory pracuje
  // s farbou.
  class Circle : Object2DBase
  {
    // Polomer kruhu.
    private double radius = 0.0;

    // Prvy konstruktor, ktory inicializuje kruh s polomerom radius
    // a defaultnou farbou.
    public Circle(double radius)
    {
      this.radius = radius;
    }

    // Druhy konstruktor, ktory inicializuje kruh s polomerom radius
    // a farbou color.
    // this(radius) zavola prvy konstruktor s parametrom radius, aby sme
    // neopakovali kod this.radius = radius;
    public Circle(double radius, Color color) : this(radius)
    {
      this.color = color;
    }

    // Implementacia vypoctu obsahu kruhu.
    // Pouzivame klucove slovicko override, ktore explicitne hovori,
    // ze prepisujeme (overridujeme) metodu calculateArea interfacu Object2D.
    public override double calculateArea()
    {
      return Math.PI * Math.Pow(this.radius, 2);
    }
  }

  // Dalsi priklad implementacie trojuholniku.
  class Triangle : Object2DBase
  {
    double a, b, c = 0.0;

    public Triangle(double a, double b, double c)
    {
      this.a = a;
      this.b = b;
      this.c = c;
    }

    public Triangle(double a, double b, double c, Color color) : this(a, b, c)
    {
      this.color = color;
    }

    public override double calculateArea()
    {
      // Heron's formula
      double s = (a + b + c) / 2;
      return Math.Sqrt(s * (s - a) * (s - b) * (s - c));
    }
  }

  class Program
  {
    // Metoda pocitajuca sumu obsahov objektov, ktore dostane na vstup v poli.
    // Vsimnite si, ze tato metoda pracuje s objektami, len cez interface
    // Object2D a vobec ju nezaujima, ci je objekt typu Circle alebo Triangle.
    // Jedine co tuto metodu zaujima je, ze tieto objekty
    // implementuju metodu calculateArea.
   public static double calculateSum(Object2D[] objects)
    {
      double sum = 0.0;
      foreach (Object2D obj in objects)
      {
        sum = sum + obj.calculateArea();
      }
      return sum;
    }

    static void Main(string[] args)
    {
      // Vytvorime pole typu Object2D, do ktoreho
      // mozeme vkladat instancie vsetkych tried, ktore
      // interface Object2D implementuju.
      Object2D[] objs = new Object2D[5];
      objs[0] = new Circle(19, Color.BLUE);
      objs[1] = new Circle(15);
      objs[2] = new Triangle(5, 5, 5);
      objs[3] = new Triangle(2, 1.5, 1.5, Color.RED);
      objs[4] = new Triangle(3, 3, 3, Color.WHITE);

      double sumOfAreas = calculateSum(objs);
      Console.WriteLine("Sum of areas: " + sumOfAreas);

      Console.WriteLine("Colors:");
      foreach (Object2D obj in objs)
      {
        Console.WriteLine(obj.getColorAsString());
      }
      Console.ReadLine();
    }
  }
}

Príklad č. 2: Objektová reprezentácia a implementácia listu

Napíšte 2 rôzne implementácie rozhrania pre štruktúru reprezentujúcu list celých čísel, tzn. int-ov podľa interfacu definovaného nižšie. Prvá implementácia musí byť spojákom (LinkedList) a druhá poľom (ArrayList).

Ak si všimnete nejaký kód, ktorý by si dal vyabstrahovať a použiť pre obe implementácie (hint: metóda print), tak ho vložte do abstraktnej triedy, ktorú si vytvorte (podobne ako u príkladu č. 1).

namespace Class3
{
    // Spolocny interface pre datovu strukturu reprezentujucu list int-ov.
    public interface IList
    {
        // Vlozi jeden prvok na koniec listu.
        void add(int element);

        // Vrati prvok v zozname na danom indexe, index zacina na 0.
        // Ak zadame neplatny index, napr. mensi ako 0 alebo vacsi ako je
        // pocet prvkov v liste, tak vrati 0.
        int get(int index);

        // Vrati pocet prvkov v liste.
        int size();

        // Odstrani prvy vyskyt prvku v liste.
        void remove(int element);

        // Vypise list na jednom riadku do konzole.
        void print();
    }

    // Implementacia listu pomocou spojoveho zoznamu.
    class LinkedList : IList
    {
        // Trieda reprezentujuca jeden uzol v zozname.
        // Triedu si kludne upravte/prisposobte, ako potrebujete.
        class Node
        {
            private int value = 0;   // ulozena hodnota v uzle.
            private Node next = null;   // referencia na dalsi prvok v zozname.
            // V tomto pripade budete pouzivat referenciu miesto pointra ako
            // to bolo v pascale. Poslednemu prvku nastavte hodnotu next
            // na null. Ci ste na konci zoznamu mozete potom kontrolovat
            // pomocout next != null

            // Konstruktor pre vytvorenie uzlu.
            public Node(int value, Node next)
            {
                this.value = value;
                this.next = next;
            }

            // Metoda pre citanie hodnoty uzlu.
            public int getValue()
            {
                return value;
            }

            // Metoda pre citanie nasledovnika.
            public Node getNext()
            {
                return next;
            }
        }

        // Koren spojaku.
        private Node root = null;

        public void add(int element)
        {
            throw new NotImplementedException();
        }

        public int get(int index)
        {
            throw new NotImplementedException();
        }

        public void print()
        {
            throw new NotImplementedException();
        }

        public void remove(int element)
        {
            throw new NotImplementedException();
        }

        public int size()
        {
            throw new NotImplementedException();
        }
    }

    // Implementacia listu pomocou pola.
    // V tomto pripade budete prvky listu ukladat do pola.
    // Kedze neviete kolko prvkov do listu pojde, tak si musite
    // toto pole najprv nainicializovat na nejaku dlzku.
    // Ak pocet prvkov v liste prekroci dlzku pola,
    // tak nainicializujete nove dlhsie pole (napr. 2 krat dlshie),
    // stare pole prekopirujete do noveho pola a pokracujete dalej.
    class ArrayList : IList
    {
        // Dlzka prveho pola, ktore inicializujete.
        private const int INIT_SIZE = 2;

        // Pole do ktoreho budete ukladat prvky listu.
        private int[] arr = new int[INIT_SIZE];

        public void add(int element)
        {
            throw new NotImplementedException();
        }

        public int get(int index)
        {
            throw new NotImplementedException();
        }

        public void print()
        {
            throw new NotImplementedException();
        }

        public void remove(int element)
        {
            throw new NotImplementedException();
        }

        public int size()
        {
            throw new NotImplementedException();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Testovaci kod.

            IList list = new LinkedList();
            // Implementacia s ArrayList-om musi davat rovnake vystupy.
            // IList list = new ArrayList();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            list.add(1);
            list.print(); // vypise "1 2 3 4 1"
            Console.WriteLine("Size:" + list.size()); // vypise 5

            list.remove(1);
            list.remove(3);
            list.print(); // vypise "2 4 1"
            Console.WriteLine("Size:" + list.size()); // vypise 3

            // vypise 4
            Console.WriteLine("Element on index 1 is: " + list.get(1));

            Console.ReadLine();
        }
    }
}

Domáca úloha č. 1

Doprogramujte úlohu č. 2 a a riešenie mi pošlite mailom. Pošlite mi zabalený (prosím .zip) projekt Visual Studia. Vaše riešenie si doneste na ďaľšie cvičenie, budeme s ním pokračovať ďalej.

Termín odovzdania: 12. 3. 2017(23:59)

Spôsob odovzdania: zdrojový kód mailom

Počet bodov: 20