Skip to content

Objekt i objekt

Till vår racerbana så har vi två klasser RaceTrack, som representerar själva racerbanan, och Car som representerar en bil på racerbanan. Vad kan vi mer behöva för klasser? Det kan till exempel behövas förare, publik och varför inte väder. Vädret kan ju påverka bilarnas hastighet.

Precis som vi har attribut i en klass som är av typen string eller int, så kan vi också ha attribut som är av en specifik klass (typen är alltså klassen). Vi skapar en klass Weather som sedan får ett attribut i klassen RaceTrack.

Vi pratar om att vädret påverkar hur fort bilarna kan köra. Klassen som ska representera väder innehåller information om vädertyp, temperatur och vindstyrka. Klassen får namnet Weather ser ut så här:

Skapa en ny klass Weather
namespace OoGuide.src;
public class Weather
{
private string _type;
private double _temperature;
private double _wind;
public Weather(string type, double temperature, double wind)
{
this._type = type;
this._temperature = temperature;
this._wind = wind;
}
public string GetForecast()
{
return $"Väder: {this._type}, {this._temperature} grader och {this._wind} m/s.";
}
public void Update(string type, double temperature, double wind)
{
// borde hämta infon från någon vädersida istället
this._type = type;
this._temperature = temperature;
this._wind = wind;
}
}

Metoden Update borde hämta informationen om vädertyp, temperatur och vindstyrka från en vädersida men för enkelhets skull så är det vi som bestämmer vädret med inparametrar.
Vi testar klassen Weather i huvudprogrammet.

Testa den nya klassen Weather i huvudprogrammet
...
Weather weather = new Weather("Soligt", 18, 8);
Console.WriteLine(weather.GetForecast());
// ger utskriften: Väder: Soligt, 18 grader och 8 m/s.
weather.Update("Halvmulet", 15, 4);
Console.WriteLine(weather.GetForecast());
// ger utskriften: Väder: Halvmulet, 15 grader och 4 m/s.

Använd klassen Weather i klassen RaceTrack

Section titled “Använd klassen Weather i klassen RaceTrack”

Klassen RaceTrack har ett attribut för väder men även om racerbanan stänger så fortsätter ju vädret att finnas. Alltså lever väderobjektet vidare även om racerbana-objektet upphör att existera. Då har vi en aggregation, vilket illustreras med en oifylld diamant ansluter till RaceTrack. Vi lägger till en metod för att lägga till ett väderobjekt till klassen AddWeather och en metod för att returnera prognosen GetForecast. Om det inte finns något väderobjekt så returnerar GetForecast bara “Väder okänt”.

Image description
Bild: Klassdiagram över RaceTrack med ett Weather objekt

Så här ser koden ut i RaceTrack:

Uppdatera klassen RaceTrack med attributet _weather
...
private Weather? _weather = null; // null för att markera inget väder satt ännu
...
public void AddWeather(Weather newWeather)
{
this._weather = newWeather;
}
public string GetWeatherForecast()
{
string forecast = "Väder okänt";
if (this._weather != null)
{
forecast = this._weather.GetForecast();
}
return forecast;
}
}

I Weather sätter vi ett defaultvärde på den private variabeln direkt när vi deklarerar variabeln _weather. Det blir tydligt och _weather är inte beroende av någon logik. Om _weather var beroende av logik och vi ville ha mer kontroll, så hade vi satt _weather till null i konstruktorn istället. Frågetecknet efter typen Weather markerar att variabeln får vara null.

Nu testar vi att använda ett objekt av klassen Weather i klassen RaceTrack.

Testa det nya attributet i klassen RaceTrack i huvudprogrammet
...
Weather weather = new Weather("Soligt", 18, 8);
weather.Update("Halvmulet", 15, 4);
RaceTrack track = new RaceTrack(100);
Console.WriteLine(track.GetWeatherForecast());
// ger utskriften: Väder okänt
track.AddWeather(weather);
Console.WriteLine(track.GetWeatherForecast());
// ger utskriften: Väder: Halvmulet, 15 grader och 4 m/s.

Naturligtvis vill vi ha med tre bilar i racet på vår racerbana. Dessa bilar skapas då racerbanan skapas och kommer bara att existera så länge racerbane-objektet finns. Relation blir då komposition. När objektet för racerbanan upphör att existera så upphör också bilobjekten att existera. Komposition är en ifylld diamant som ansluter till ägandeklassen - i detta fall RaceTrack.

Vi lägger till attributet _carsi klassen RaceTrack och det blir en lista av Car objekt. Med metoden CreateRace skapas ett race med tre bilobjekt.

Image description
Bild: Klassdiagram över RaceTrack. `List<Car>` betyder att `_cars` är en lista med Car objekt.

Så här ser koden för RaceTrack ut nu:

Klassen RaceTrack ser ut så här nu:
namespace OoGuide.src;
class RaceTrack
{
private const string NAME = "DBWEBB Racing track";
private const int MINIMUM_FINISH = 20;
private int _finishLine;
private Weather? _weather = null; // null för att markera inget väder satt ännu
private List<Car> _cars;
public RaceTrack(int finishLine)
{
this._finishLine = finishLine;
if (finishLine < RaceTrack.MINIMUM_FINISH)
{
this._finishLine = RaceTrack.MINIMUM_FINISH;
}
this._cars = new List<Car>();
this.CreateRace();
}
// Other methods
public void CreateRace()
{
Car car1 = new Car("Volvo", 99000, "Passenger");
this._cars.Add(car1);
Car car2 = new Car("Ferrari", 899000, "Sport");
this._cars.Add(car2);
Car car3 = new Car("Ford Kuga", 299000, "SUV");
this._cars.Add(car3);
}
public void Description()
{
Console.WriteLine($"\nVälkommen till {RaceTrack.NAME}!");
Console.WriteLine($"{this._weather.GetForecast()}");
Console.WriteLine($"Det finns {this._cars.Count} bilar:");
foreach (var aCar in this._cars) // kan använda var eller Car som datatyp
{
aCar.Description();
}
this.PrintFinishLine();
}
}

I RaceTrack har vi nu en metod CreateRace som lägger till tre bilar, det vill säga tre objekt av typen Car, till racerbanan. Dessutom utökar vi en metoden Description för att även få information om vädret och om bilarna. Vi förutsätter att raderna för att testa väderobjektet är kvar.

Lägg till följande rad i huvudprogrammet och kör programmet
...
track.Description();
// Kör dotnet run i terminalen och det ger utskriften:
// Välkommen till DBWEBB Racing track!
// Väder: Halvmulet, 15 grader och 4 m/s.
// Det finns 3 bilar:
// Bilen är en Volvo och kostar 99000 kr.
// Bilen är en Ferrari och kostar 899000 kr.
// Bilen är en Ford Kuga och kostar 299000 kr.
//
// START MÅL
// |

Ny klass AsciiArt som används i klassen Car

Section titled “Ny klass AsciiArt som används i klassen Car”

Det blir lite roligare med en grafisk representation av olika typer av bilar, till exempel en för passagerarbil, en för SUV och en för sportbil. Klassen kommer inte att ändra tillstånd och blir därför statisk och anropas därmed via klassnamnet.

Här är tre ASCII-bilder som vi kan “rita” ut i terminalen. Vi har tre fina ascii bilder för bilar som vi kan lägga till i Car klassen. Vi gör en metod som returnerar en ascii bild för “Passenger”, en annan för “Suv” och en tredje för “Sport”. Vi lägger en variabel pos framför bilderna för att kunna flytta på dem senare.

Lägg till en ny klass AsciiArt
namespace OoGuide.src;
public static class AsciiArt
{
public string GetAsciiModel(string type, string pos = "")
{
if (type.ToLower() == "sport")
{
return $@"
{pos}
{pos}.-'--`-._
{pos}'-O---O--'
";
}
else if (type.ToLower() == "suv")
{
return $@"
{pos} ______
{pos} /|_||_\`.__
{pos}( _ _ _\
{pos}=`-(_)--(_)-'
";
}
else // Passenger
{
return $@"
{pos} __
{pos} _| =\__
{pos}/o____o_\
";
}
}
}

I den här C#-koden med @-symbolen före strängen så tillåts du att skriva en ordagrann sträng, vilket innebär att tecken, radbrytningarna och formateringen bevaras som de är i koden. Till exempel blir @“\n” en sträng med tecknet backslash följt av tecknet n istället för en ny rad som det kan bli utan @.

Vi lägger till ett nytt attribut till Car som beskriver dess bilmodell eller typ och uppdaterar konstruktorn med typen. Vi lägger ett defaultvärde på typen så att det är en passagerarbil om inget anges.

Vi lägger till en ny metod PrintAsciiModel i klassen Car som använder en metod i klassen AsciiArt för hämtar en bild av en biltyp. I det här fallet blir relationen av den svagare typen association som representeras med en pil från klassen som använder den, dvs i det här fallet från Car mot AsciiArt.

Image description
Bild: Klassdiagram över RaceTrack med komposition, aggregation och association

Så här uppdaterar vi koden:

Uppdatera klassen Car med metoden PrintAsciiModel
...
private string _type;
public Car(string model, int price, string type="Passenger")
{
this._model = model;
this._price = price;
this._type = type;
Car._carCount += 1;
}
...
public void PrintAsciiModel(string pos = "")
{
Console.WriteLine(AsciiArt.GetAsciiModel(this._type, pos));
}
}

Vi utökar metoden Description på klassen RaceTrack för att skriva ut ASCII-bilden för respektive bil.

Utöka metoden Description i klassen RaceTrack
namespace OoGuide.src;
class RaceTrack
{
...
public void Description()
{
Console.WriteLine($"\nVälkommen till {RaceTrack.NAME}!");
Console.WriteLine($"{this._weather.GetForecast()}");
Console.WriteLine($"\nDet finns {this._cars.Count} bilar:");
foreach (var aCar in this._cars) // kan använda var eller Car som datatyp
{
aCar.Description();
aCar.PrintAsciiModel();
}
this.PrintFinishLine();
}
}

När vi nu kör huvudprogrammet får vi följande utskrift:

Kör huvudprogrammet
...
track.Description();
// Kör dotnet run i terminalen och det ger utskriften:
// Välkommen till DBWEBB Racing track!
// Väder: Halvmulet, 15 grader och 4 m/s.
// Det finns 3 bilar:
// Bilen är en Volvo och kostar 99000 kr.
// __
// _| =\__
// /o____o_\
//
// Bilen är en Ferrari och kostar 899000 kr.
// .-'--`-._
// '-O---O--'
//
// Bilen är en Ford Kuga och kostar 299000 kr.
// ______
// /|_||_\`.__
// ( _ _ _\
// =`-(_)--(_)-'
//
// START MÅL
// |

Vi gör en metod av all ny kod från detta kapitel i Program.cs och kallar den TestObjectInObject.

Strukturera koden genom att lägga den i en metod som anropas i Program.cs
...
static void TestObjectInObject()
{
Weather weather = new Weather("Soligt", 18, 8);
Console.WriteLine(weather.GetForecast());
weather.Update("Halvmulet", 15, 4);
Console.WriteLine(weather.GetForecast());
RaceTrack track = new RaceTrack(100);
Console.WriteLine(track.GetWeatherForecast());
track.AddWeather(weather);
Console.WriteLine(track.GetWeatherForecast());
track.Description();
}
TestObjectInObject();

När vi inte vill använda metoden TestObjectInObject kommenterar vi bort den.

Se här ser vårt klassdiagram ut nu:

Image description
Bild: Klassdiagram över RaceTrack med komposition, aggregation och association

Vi har en komposition mellan RaceTrack och Car, vilket innebär när RaceTrack objektet upphör att existera så upphör även Car-objekten att existera. Däremot fortsätter Weather-objektet att existera då RaceTrack objektet upphör att existera eftersom dess relation är aggregation. Det finns även relationen association mellan Car och AsciiArt.

Vi har använt objekt i objekt med olika relationer.