Opis aplikacji
W ramach ćwiczeń napisałem algorytm, który wyznacza trasy w grafie pomiędzy jednym punktem, a drugim. Napisałem więc klasę bazową:
public class Point
{
public int X { get; protected set; }
public int Y { get; protected set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public Point()
{
this.X = 0;
this.Y = 0;
}
public void UpdateLocation(int x, int y)
{
this.X = x;
this.Y = y;
}
}
klasę wierzchołka:
public class Vertex : Point
{
public IEnumerable<Vertex> Edges { get; private set; }
public Vertex(int x, int y) : base(x, y)
{
}
public Vertex() :base(0,0)
{
}
public void AddEdge(IEnumerable<Vertex> edges)
{
this.Edges = edges;
}
}
oraz klasę grafu:
public class Graph
{
public virtual List<Vertex> Vertexes { get; private set; } = new List<Vertex>();
public Graph(List<Vertex> vertexes)
{
this.Vertexes = vertexes;
}
public Graph()
{
}
public virtual void AddVertex(Vertex v)
{
Vertexes.Add(v);
}
public virtual void AddVertex(int x, int y)
{
Vertex v = new Vertex(x, y);
AddVertex(v);
}
public Vertex GetVertex(int x, int y)
{
if (Vertexes.Count == 0)
return null;
Point searchFrom = new Point(x, y);
Vertex temp = Vertexes.OrderBy(p => Helper.Distance(p, searchFrom)).FirstOrDefault();
if (Helper.Distance(searchFrom, temp) < (Settings.CircleSize / 2))
return temp;
return null;
}
public virtual void RemoveVertex(Vertex v)
{
Vertexes.Remove(v);
}
public IEnumerable<Graph> FindPathsBetween(Vertex start, Vertex end)
{
// main code
}
}
I wszystko działa poprawne. Twoszę sobię instancje wierzchołków, następnie dodaję im połączenia z innymi wierzchołkami i używając FindPathsBetween
dostaję trasę pomiędzy jednym, a drugim wierzchołkiem.
Co chcę osiągnąć
Zdecydowałem, że chciałbym przerobić program tak, by korzystał z GUI. Utworzyłem więc nowy projekt z wykorzystaniem WindowsForms
oraz dodałem funkcjonalność dodawania wierzchołków poprzez kliknięcie na PictureBox
. Wszystko działało poprawnie, jednak postanowiłem, że fajnie by było, gdyby po kliknięciu w PictureBox
zamiast samej elipsy pojawiał się na niej tekst z numerem indeksu, czyli zamiana z czegoś takiego:
na:
Oczywiście w bardzo prosty sposób udało mi się to osiągnąć lekko modyfikując klasę Graph
oraz Vertex
. Wystarczyło dodać właściwość Id
do wierzchołka, a w klasie Graph
przechowywać informację o wolnym numerze wierzchołka.
Pierwsze wątpliwości
Aby uzyskać indeksowanie wierzchołków musiałem zmodyfikować klasę Graph
oraz Vertex
. Gdybym teraz w innej aplikacji chciał wykorzystać te klasy, to ich wierzchołki musiałby mieć indeksy (co mogłoby być niepotrzebne). Wydaje mi się, że to co zrobiłem jest złe. Wcześniej te klasy były bardziej ogólne. Teraz każda instancja obiektu Vertex
posiada swój id dodany na potrzeby tylko tego, bym mógł zobaczyć to na GUi.
Dążę do tego, że wydaje mi się, że w momencie, kiedy chcę dodać do klasy jakąś nową funkcjonalność niekoniecznie związaną z tą klasą powinienem sworzyć nową klasę, która dodaje mi taką możliwość (i np. używa dziedziczenia). No przecież klasy Graph
oraz Vertex
mógłbym dostać w formie biblioteki i nie miałbym możliwości ich edytowania.
Stwierdziłem, że pozostawię klasy Graph
oraz Vertex
w takiej formie jakiej są, czyli najbardziej ogólnej. Skoro chcę ponazywać każdy wierzchołek, to mogę przecież stworzyć nową klasy wzbogacone o nowe właściwości i dziedziczyć po Graph
oraz Vertex
. W takim razie dodałem klasę FormVertex
:
class FormVertex : Vertex
{
public int Id { get; private set; }
public FormVertex()
{
}
public FormVertex(int id, int x, int y) : base(id, x, y)
{
this.Id = id;
}
}
A klasa FormGraph
jest przecież identyczna jak Graph
. Ma mieć te same metody. Jedyna różnica jest taka, że zamiast przechowywać informacje o Vertex
będę przechowywał FormVertex
. Potrzebuję jeszcze właściwości int NextVertexId
, która będzie inkrementowana po dodaniu nowego wierzchołka.
Jaki jest problem
Dostaję błędy związane typowaniem. Klasa Graph
w konstruktorze oczekuje listy typu Vertex
, a do kontruktora FormGraph
przesyłam przecież listę FormVertex
.
Nie wiem również, czy sprawę dodawania FormVertex
rozwiązałem poprawnie. Jak widać do base.AddVertex(fv);
prześlę referencję do instancji fv
, jednak będzie ona typu Vertex
, a nie FormVertex
(rzutowanie w górę). Jednak w klasie formularza będę mógł zrobić coś takiego:
foreach (var vertex in graph.Vertexes)
if (v is FormVertex)
{
FormVertex fv = (FormVertex)v;
// dostep do indeksu mam poprzez fv.Id;
}
Macie więc jakieś wskazówki jak mogę rozwiązać moje problemy? Głównie chodzi mi o to, jak rozwiąząć sytuację z błędami typowania. Być może rozwiązanie jest bardzo proste, ale siedzę nad tym już tyle, że nie mam na nic pomysłu. Być może w ogólę źle podchodzę do problemu i zamiast dziedziczenia powinienem skorzystać z czegoś innego. Poszę wziąć pod uwagę to, że jestem początkujący w kodowaniu.