Objectgeoriënteerd Programmeren: WPO 2a 1. Inhoud Eenvoudige (enkelvoudige) overerving, override, ToString(), base, private, public, protected, virtual 2. Inleiding 2.1 Overerving In het voorgaande WPO hebben we gezien hoe we een klasse konden schrijven en op welke manier men deze tot een object kon instantiëren. In de oefeningen die daarop volgden werd er telkens gebruik gemaakt van 1 enkel type object. In grotere programma s kan het echter voorkomen dat men tijdens het schrijven van verschillende klassen opmerkt dat een aantal klassen gemeenschappelijke of gelijkwaardige code bevatten. Als deze klassen een gelijkwaardige functionaliteit implementeren (bv. ze moeten allemaal een vorm tekenen), kan het nuttig zijn om deze gemeenschappelijke functionaliteiten onder te brengen in een gemeenschappelijke klasse en deze klasse over te erven door andere klassen die elk een specifiek deel implementeren (cirkel, vierkant, enz.). Tijdens deze inleiding zullen we een tekenprogramma implementeren die een aantal vormen op het scherm kan tekenen. 2.1.1 Paint Schrijf een programma dat een vierkant en cirkel kan tekenen. In de eerste fase zal je een aparte klasse schrijven voor het vierkant en een aparte klasse voor de cirkel. Beide vormen beschikken over een grootte, een kleur en een positie (Point). Momenteel wordt nog geen gebruik gemaakt van overerving! 2.1.2 Shape De klasse cirkel en de klasse vierkant hebben een aantal gemeenschappelijke eigenschappen: de positie, de grootte, de kleur. Het enige wat verschilt voor beide objecten is de vorm en dus het tekenen van het object op het scherm. Hoewel de huidige constructie heel legitiem is, is deze niet handig: gelijkwaardige code wordt niet hergebruikt, indien er een fout in de gekopieerde code voorkwam en is opgemerkt, moet deze in alle stukken code rechtgezet worden. 1
Daarom zullen we de gemeenschappelijke code onderbrengen in een gemeenschappelijke klasse. Deze klasse wordt ook een superklasse genoemd. Schrijf de klasse Shape waarin deze eigenschappen ondergebracht worden. Laat de 2 andere klassen hiervan overerven. Deze klassen zijn de kindklassen. De 2 klassen behouden weliswaar hun respectievelijke tekenfunctie. 2.1.3 Uitbreiding Het programma dat we nu hebben is geschikt om 2 type figuren weer te geven op het scherm. Maak 2 lijsten aan om de cirkels en vierkanten bij te houden. Zorg dat je ook alle getekende objecten kan weergeven in een listbox. Dit doe je door over alle objecten te loopen en de eigenschappen van de objecten te casten naar een string. 2.1.4 override ToString() Alle eigen geschreven klassen erven over van de klasse object. Deze klasse voorziet een aantal basisfunctionaliteiten. Een ervan is de methode ToString(). Door deze aan te roepen vraag je tekstuele informatie betreffende dat object. Deze methode is generiek en zolang men deze niet overschrijft zal deze ook weinig nuttigs teruggeven. Overschrijf (override) deze methode in de klassen cirkel en vierkant en zorg ervoor dat nuttige informatie teruggegeven worden. Een voorbeeldcode kan gevonden worden in codefragment 1. Deze manier wordt ook overriding genoemd en wordt gebruikt om bestaande methode met dezelfde naam te overschrijven. Codefragment 1: ToString() methode overriden. 1 public string override ToString() 2 { 3 return "Circle on position X = " X.ToString() + ", Y = " + Y.ToString(); 4 } Deze methode heeft een belangrijk voordeel: nu beschikken de objecten in onze code over een automatische manier om informatie in tekstuele vorm weer te geven. Als voorbeeld print je een object af in een MessageBox. Verwijder nu de loop die nodig was om de informatie in een listbox weer te geven, en wijs de lijst nu rechtstreeks toe aan de listbox. Opgelet: Het overriden van dergelijke methoden kan pas indien de superklasse over exact dezelfde methode beschikt en dat die methode als virtual wordt gedeclareerd. Codefragment 2: ToString() virtual methode in de superklasse. 1 public virtual string override ToString() 2 { 3 return "Objectname"; 4 } Dit mechanisme kan gebruikt worden in een eigen ontwerp. Indien men een generieke methode aanlevert maar men verwacht dat deze overschreven zal worden, kan men deze als virtual declareren. 2
2.1.5 Constructors Constructors zijn de eerste methoden die aangeroepen worden wanneer een object geïnstantieerd wordt. Constructors retourneren geen enkele worden, maar kunnen een willekeurig aantal argumenten opnemen. Constructors worden niet overgeërfd, maar worden opgeroepen door de kindklasse. Wanneer de superklasse over constructors beschikt die argumenten opnemen, moet deze contructor door de kindklasse expliciet opgroepen worden. In het andere geval volstaat een impliciete oproep, en hoeft die niet in de code vermeld te worden. Dit wordt weergegeven in codefragment 3. 3
Codefragment 3: Constructors en overerving 1 //--------------------------------------------------------- 2 public class Figures 3 { 4 public Figures() {// do something} 5 public Figures(int amount) { // do something} 6 7 //--- other code 8 } 9 //--------------------------------------------------------- 10 public class Ball:Figures 11 { 12 // this constructor calls the super constructor without arguments since it is allowed 13 // this call is implicit!!! 14 public Ball() { // do something} 15 } 16 //--------------------------------------------------------- 17 public class Rectangle:Figures 18 { 19 // this constructor calls the super constructor with arguments 20 // this call is explicit!!! 21 public Rectangle( int amount):base(amount) { // do something} 22 } Schrijf de nodige constructors in de 3 klassen. 2.1.6 Zichtbaarheid Naast public en private is er een vorm van zichtbaarheid die enkel invloed heeft tijdens het overerven: protected. Hieronder worden de volgende de verschillende vormen van zichtbaarheid opgesomd. Private: enkel zichtbaar binnen de eigen klasse. Niet zichtbaar buiten de klasse en ook niet zichtbaar binnen kindklassen. Protected: enkel zichtbaar binnen de eigen klasse en binnen de kindklassen. Public: zichtbaar voor iedereen. 2.1.7 Klassendiagram Visual Studio laat toe om een klassendiagram te tekenen a.d.h.v. de bestaande code. In dit soort van diagrammen worden de verbanden tussen de verschillende klassen weergegeven waardoor eventuele problemen gevisualiseerd kunnen worden. Maak een dergelijk diagram aan. 4
2.2 Samenvatting Overerving zorgt ervoor dat gemeenschappelijke en gelijkwaardige code in een gemeenschappelijke klasse onderbracht kan worden. Deze klasse wordt de superklasse genoemd. De klassen die van deze klasse overerven worden de kindklassen genoemd. Merk op dat een kindklasse op zijn beurt ook een superklasse kan zijn! Tijdens het overerven kan het voorkomen dat bepaalde methoden overschreven dienen te worden. Een bekende methode is ToString(). Soms worden die als overschrijfbaar gemarkeerd doordat deze methoden als virtual aangeduid worden. Bestaande methoden overschrijven vindt plaats door deze te overriden. Constructors worden niet overgeërfd. Deze worden impliciet of expliciet vanuit de kindklasse aangeroepen. Een expliciete oproep met de juiste argumenten is verplicht indien de superklasse niet over een constructor zonder argumenten beschikt. Soms kan het gebeuren dat een methode een methode van de superklasse moet aanroepen ondanks dat de klasse over dezelfde methode beschikt. In dit geval moet de methode voorafgegaan worden door base. 3. Oefeningen Demo 1: Meterstand Demo 2: Horizontale meterstand A: Softwarelibrary A: Rekenmachine A: Average E: Grafiek 3.1 Demo 1: Meterstand In deze opdracht wordt er een eigen component gemaakt die toelaat om de stand van een bepaalde sensor weer te geven. De component die we zullen behandelen ligt in het verlengde van het tekenen dat we tot hiertoe gewend zijn. Hoewel de wijze van tekenen zoals we die gewoon zijn redelijk goed is, heeft deze 2 nadelen. De code behoorde niet tot een eigen klasse. geschreven. Deze werd rechtstreeks in het formulier Het tekenen zorgde ervoor dat er flikkering ontstond. Om deze tekortkomingen tegen te gaan zullen we een eigen klasse schrijven. C# biedt de mogelijkheid om een eigen control aan te maken net zoals een button, panel, e.d. Eens deze component afgewerkt is, volstaat het deze net zoals een andere gekende component op het formulier te slepen. In codefragment 4 wordt de basis van een eigen (teken)control weergeven. 5
Codefragment 4: Basisstructuur van een usercontrol (met tekenen). 1 public partial class Figuren : UserControl 2 { 3 public Figuren() 4 { 5 InitializeComponent(); 6 // enable double buffering -> avoids flickering 7 this.setstyle( 8 ControlStyles.AllPaintingInWmPaint 9 ControlStyles.UserPaint 10 ControlStyles.DoubleBuffer, 11 true); 12 // avoiding flickering is only possible when we use the Paint event, so use it! 13 this.paint += Repaint; 14 // the refresh function forces our control to redraw everything in the paint function 15 this.refresh(); 16 } 17 /*----------------------------------------------------------------*/ 18 public void UpdateControl() 19 { 20 // force a refresh 21 this.refresh(); 22 } 23 /*----------------------------------------------------------------*/ 24 private void Repaint(object sender, System.Windows.Forms.PaintEventArgs e) 25 { 26 // draw the things as we always do 27 Graphics g = e.graphics; 28 g.clear(color.white); 29 Brush br = new SolidBrush(Color.Red); 30 g.fillrectangle(br, 10, 10, 100, 100); 31 g.drawstring("bla", this.font, br, 250, 250); 32 br.dispose(); 33 } 34 } Zorg ervoor dat je een meter kan bouwen zoals weergegeven in figuur 1. Deze meter wordt ingesteld a.d.h.v. een minimum- en maximum waarde. Deze eigenschappen zijn ook te raadplegen in de visuele editor van Visual Studio. (zie codefragment 5 als voorbeeld). Als uitbreiding kan je ook de eigenschappen toevoegen om een aantal kleuren binnen de grafiek aan te passen en om tekst te tekenen op de control. Gebruik een randomgenerator als input voor deze control. 6
Codefragment 5: Eigenschappen van de eigen control toevoegen in de properties venster binnen Visual Studio. 1 [System.ComponentModel.Browsable(true), // enable this in order to have it in the Visual Studio Properties Listing 2 System.ComponentModel.Category("Layout"), // set in the category of layout 3 System.ComponentModel.Description("Sets or gets the color of the control")] 4 public Color kleur 5 { 6 get { return this.kleur_; } 7 set 8 { 9 if (value!=null) 10 { 11 this.kleur_ = value; 12 } 13 } 14 } Figuur 1: Voorbeeld van een meter. 3.2 Demo 2: Horizontale meterstand Herneem voorgaande demo. Zorg ervoor dat je naast een verticale meter ook een horizontale meter kan tekenen. Probeer hierbij zoveel mogelijk methoden van de voorgaande oplossing over te nemen via overerving. Virtual methoden kunnen ook ingevoerd worden om een aantal zaken te vereenvoudigen. 7
3.3 A: Softwarelibrary Herneem de opdracht van de softwarelibrary van het voorgaande WPO. Pas het programma aan zodat de listbox automatisch wordt aangevuld wanneer een item aan de lijst wordt toegevoegd. Gebruik hiervoor de methode ToString(). 3.4 A: Rekenmachine Herneem de opdracht van het rekenmachine van voorgaande WPO. Pas het op dezelfde manier aan zoals de voorgaande opdracht. 3.5 A: Average Schrijf een applicatie die het mogelijk maakt om een gemiddelde te berekenen van een aantal ingevoerde waarden. Hierbij is het de bedoeling dat je de List overerft. Telkens een waarde in de lijst wordt toegevoegd, wordt het gemiddelde automatisch berekend. Ook de kleinste en de grootste waarde worden uit deze lijst met waarden bepaald. 3.6 E: Grafiek In figuren 2 en 3 kunnen een aantal grafieken gevonden worden. Schrijf zelf een usercontrol met alle nodige eigenschappen om een dergelijke grafiek te tekenen. Voorzie als input een sequentie van random gegenereerde waarden. Grafieken worden bepaald door een minimum- en maximumwaarde, de kleur, een eenheid, enz. Zorg ervoor dat de gemeenschappelijke eigenschappen en methoden in een superklasse ondergebracht worden. Specifieke methoden worden ondergebracht in de kindklassen (o.a. het tekenen). Figuur 2: Voorbeeld van een horizontale grafiek. 8
Figuur 3: Voorbeeld van een polar plot. 9