Witam.
W charakterze "wprawki" piszę sobie grę rougelike i napotkałem na pewien problem:
Dla przykładu - mam między innymi klasy:
public abstract class GameObject
{
public Point position;
public abstract void load(string objectData); // ma stworzyć obiekt na podstawie linijki tekstu (odczytanej z pliku) <=====
public abstract string save(); // ma zapisać zawartość obiektu do linijki tekstu (zapisanej później do pliku)
public abstract draw();
//......
}
Od razu pytanie numer 1: Czy da się wymusić, aby wszystkie klasy potomne posiadały konstruktor przyjmujący 1 argument typu string?
public class GameObjectDoor:GameObject
{
public bool isOpen;
public override void load (string objectData) {/*....*/};
public override string save() {/*....*/};
//......
}
public class Creature:GameObject
{
public int hitPoints;
public int maxHitPoints;
public override void load (string objectData) {/*....*/};
public override string save() {/*....*/};
//......
}
I tak dalej, i tak dalej. Z klasy Creature z kolei dziedziczyć będą później np. klasy Wolf, Rat i Dragon. Ogólnie - tych klas będzie dużo. Chcę, żeby to było rozszerzalne.
Najbardziej interesują nas w tym momencie metody string save() i void load(string) - chodzi o to, aby zapisać wszystko do pliku. Przyjąłem sobie, że w tym pliku każda linijka odpowiada jednemu obiektowi, i zaczynać się będzie od nazwy typu (oczywiście, można to zmienić).
Wszystkie obiekty przechowywane są w liście:
List<gameObject> objectsOnMap;
Zapis gry do pliku jest w tej sytuacji zaskakująco trywialny:
public void save(string fileName)
{
StreamWriter sW = new StreamWriter(fileName);
foreach (GameObject obj in objectsOnMap)
{
sW.WriteLine(obj.save());
}
sW.Close();
}
Problem pojawia się przy odczycie - na chwilę obecną wygląda to tak:
public void load(string fileName)
{
StreamReader sR = new StreamReader (fileName);
string line;
while (sR.Peek() >= 0)
{
line = sR.ReadLine();
switch(line.Split('\t')[0])
{
case "GameObjectDoor":
objectsOnMap.Add(new GameObjectDoor(line))
break;
case "Creature":
objectsOnMap.Add(new GameObjectDoor(line))
break;
}
}
sW.Close();
}
STRASZNIE nie podoba mi się ten switch w tej funkcji - powoduje to, że za każdym razem, gdy będę dopisywał nową klasę obiektu, będę musiał (oprócz napisania samej klasy, oraz metod do zapisu/odczytu z/do stringa dla niej) , zmodyfikować także zupełnie inną klasę, gdzie znajduje się definicja funkcji czytającej listę obiektów z pliku. Mam nieodparte wrażenie, że dałoby się całość zorganizować tak, aby wczytywanie było równie "eleganckie", jak zapis.
Pytanie numer 2: Czy da się to jakoś rozwiązać, tak, aby jedyne zmiany w kodzie były tylko w klasie potomnej?
Pytanie numer 3: Czy da się iterować po wszystkich typach (!) dziedziczących z GameObject? (pewnie się nie da, ale nie zaszkodzi spytać)
Pytanie numer 4:
Mamy klasy:
class GameObject {…}
class Door:GameObject {…}
class Monster:GameObject {…}
class Dog:Monster {….}
class Rat:Monster
I funkcję:
void foo(GameObject obj)
Pytanie numer 4: Jak napisać warunek IF, który będzie spełniony, jeżeli zmienna obj jest typu Monster (czyli także Rat lub Dog)?