Pod kątem inżynieryjnym najlepsze jest posiadanie dobrego powodu do zrobienia czegoś. :)
Czemu chcesz zabronić tworzenia obiektu przez new
? Boisz się, że ktoś zrobi obiekt nieprawidłowo, podając złe parametry? W takim celu wystarczy walidacja w konstruktorze. Czasami może mieć sens konstruktor prywatny + publiczna statyczna metoda fabrykująca jak wspomniał @mstl. Ale do tego też trzeba mieć powód, żeby nie zrobić tylko prostego wrappera na konstruktor.
Więc tak. Na początku chciałem mieć fabrykę generyczną. W stylu:
Kopiuj
Item i = Factory.Instance.CreateItem<T>(jakiesParametry);
A skoro za tworzenie obiektu ma odpowiadać fabryka, to chcę uniemożliwić tworzenie przez new, bo w przyszłości coś się może rozjechać. Niech teraz ktoś coś utworzy przez new w kilku miejscach w kodzie. A za dwa miesiące zmieni się ciało metody CreateItem w fabryce w jakimś stopniu. Tu zachodzi obawa, że wtedy tamte obiekty tworzone przez new będą w jakiś sposób źle skonstruowane. Dlatego też chcę zabronić tworzenia tych obiektów przez new. W ostateczności mógłbym zrobić to tak, że konstruktor przyjmowałby obiekt fabryki i sprawdzał, czy fabryki są sobie równe, coś w stylu:
Kopiuj
public ConcreteItem(Factory f, inneParametry, [CallerMemberName] string memberName = "")
{
if((f != Factory.Instance) || (memberName != "CreateItem))
throw new Exception("Nie o takie Polskie walczyłem!");
}
no, ale sam widzisz, co to za kod ;)
Nie dość, że naprawdę kiepski to jeszcze zmniejsza możliwość nieprawidłowego utworzenia, ale nie eliminuje jej.
Pomysł z budowniczym, gdzie klasa jest wewnętrzną klasą budowniczego jest niezły, ale tych klas będzie kilka i chciałbym, żeby były normalnymi klasami, a nie wewnętrznymi.
I teraz załóżmy taką sytuację. Mam 3 klasy konkretne: A, B i C.
Tworzenie każdej z nich może (choć pewnie nie będzie) być lekko inne, np:
Kopiuj
A a = new A(p); //jakiś parametr
if(a.cosTam)
jakisInnyObiekt.Add(a);
B b = new B();
InnaKlasa k = new InnaKlasa();
C c = new C(k);
jakisInnyObiekt.Add(c);
Wiem, że w tym przypadku mógłbym porobić po prostu odpowiednie konstruktory, ale:
- utrudni to tworzenie tych obiektów i przy okazji zaciemni nieco kod
- nie chcę żeby te klasy wiedziały cokolwiek o "jakisInnyObiekt" - mają być zupełnie rozdzielone
- to tylko pierwszy, lepszy przykład o jakim pomyślałem, w rzeczywistości może to wyglądać zupełnie inaczej
I tu najlepszym rozwiązaniem wydaje się być takie połączenie fabryki z budowniczym.
Żeby klient wywołał tylko:
Kopiuj
Item a = Factory.Instance.CreateItem<A>();
Item b = Factory.Instance.CreateItem<B>();
Item c = Factory.Instance.CreateItem<C>();
I tu nie ma problemu, że jakiś element zostanie źle utworzony i rozwali program. I klienta nie interesuje jak jest obiekt tworzony.
Zaznaczam, że póki co nie mam żadnego konkretnego problemu, po prostu patrzę dość przyszłościowo (że problem wymagający innego tworzenia obiektu może się pojawić z czasem). Chcę od samego początku dobrze przemyśleć ten mechanizm i zaimplementować. Żeby w razie czego nie trzeba było przekopywać połowy aplikacji.
W C++ użyłbym po prostu friendsów do tego. I nie byłoby sprawy. Ale w C# nie ma takiego mechanizmu, więc muszę wykombinować coś innego ;)
Najprostszym i chyba najbardziej naturalnym rozwiązaniem wydaje się tu refleksja. I tego bym użył, gdyby refleksja nie była tematem sporów. Więc szukam innych sposobów na osiągnięcie celu, jednak mam też pewne własne ograniczenia - jak np. to, że chcę żeby te klasy nie były zadeklarowane wewnątrz innych.