Fabryka i refleksja

Fabryka i refleksja
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

Cześć, chcę żeby obiekty pewnych klas tworzyła mi tylko i wyłącznie fabryka:

Kopiuj
abstract class MyClass
{

}

class ConcreteClassA: MyClass
{

}

class ConcreteClassB: MyClass
{

}

Czyli taki kod ma być w ogóle niedopuszczony przez kompilator (tylko fabryka może coś takiego zrobić):

Kopiuj
ConcreteClassA obj = new ConcreteClassA();

Pomysłów na to mam kilka, ale każdy ma swoje plusy i minusy, np:

  1. Klasy mają zdefiniowane konstruktory internal w jednej dll i tylko fabryka (która też jest w tej dll) może je tworzyć. Zwrócenie obiektu następuje za pomocą interfejsu. Minusem jest to, że muszę mieć dodatkową dllkę do kilku klas, które i tak są jądrem całej aplikacji.

  2. Klasy mają prywatne konstruktory, a fabryka używa refleksji do ich utworzenia. Z jednej strony jest to dobre rozwiązanie, ponieważ robi dokładnie to, co chcę, ale z drugiej mamy tu refleksję. Z trzeciej strony ;) skoro dostaliśmy refleksję, fajnie by było czasem jej użyć.

  3. Miałem jeszcze jakiś pomysł z zagnieżdżaniem fabryki w klasach, ale to od razu eliminuje.

Tak więc, który sposób wydaje się Wam najlepszy? Kilka klas + fabryka w osobnej dll? A może jednak ta refleksja tu będzie lepsza? Ilość tych klas będzie niewielka i raczej nie będzie się zmieniać. To 3, góra 5 klas. Jednak po klasie MyClass mogą też dziedziczyć inne klasy, które będą już mogły być tworzone normalnie przez new.

edytowany 1x, ostatnio: Juhas
MA
  • Rejestracja:ponad 9 lat
  • Ostatnio:ponad rok
  • Postów:24
0

zawsze możesz skompilować expression i tym samym ponieść koszt refleksji tylko raz
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/

JU
Hmmm, nie wiedziałem o tym. Ale bardziej mi zależy jak to ugryźć od strony inżynieryjnej. Tych obiektów nie będzie na tyle dużo, żeby przejmować się narzutem refleksji.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Wrocław
4

A jaki jest cel takiej kombinacji? Bo wygląda jak coś, co szybko się zemści na autorze albo jego kolegach z pracy.

T9
Może mają jakieś magiczne singletony i static które trzeba obsługiwać.
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

Nie, dlaczego? Po prostu chcę mieć pewność, że obiekt nie zostanie utworzony przez new, a tylko i wyłącznie za pomocą fabryki. Po to ma być fabryka, żeby nie tworzyć obiektów przez new. Chcę to zablokować.

Tak naprawdę to możliwe, że będzie to ostatecznie builder, a nie fabryka. Dlatego tak ważne dla mnie jest, żeby nie tworzyć tych obiektów przez new. Żeby kompilator na to nie pozwolił.

MS
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 7 godzin
  • Postów:312
1

@Juhas:

chcę mieć pewność, że obiekt nie zostanie utworzony przez new

To nie wystarczy prywatny konstruktor oraz statyczna metoda Create w klasie zwracająca instancję?


JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

No jest to jakieś rozwiązanie. Zastanawiam się tylko, czy fabryka nie byłaby lepsza pod kątem inżynieryjnym.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Wrocław
3

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.

MS
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 7 godzin
  • Postów:312
0

@Juhas zapoznaj się może z takim rozwiązaniem:
http://www.codinghelmet.com/?path=howto/advances-in-applying-the-builder-design-pattern
Takie rozwiązanie co prawda nadal nie uniemożliwia używać new, ale i to da się obejść. Nawiązując trochę do przykładu z linku - publiczny powinien być tylko interfejs IPerson, a konkretna klasa powinna być zagnieżdżona w klasie Builder:

Kopiuj
public interface IPerson
{
	string Name { get; }
}

public class Builder
{
	public IPerson Build()
	{
		return new Person()
		{
			Name = "Juhas"
		};
	}

	private class Person : IPerson
	{
		public string Name { get; set; }
	}
}

Tylko zastanów się, czy na pewno czegoś takiego potrzebujesz ;)


edytowany 2x, ostatnio: mstl
JU
No pomysł dobry, ale chciałbym, żeby Person nie była klasą wewnętrzną.
FP
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 7 lat
  • Postów:132
0

Wydaje mi się ze dobrym pomysłem jest konstruktor internal; i zamknąć całość w dll'ce.
Ew klasyczny factory patern.
Autor wspomniał że obiekty są używane w całym projekcie...ale chyba using'i nie stanowią problemu, chociaż ... cierpi tak zw "decoupling".

Albo już wspomniany prywatny konstruktor i statyczna "metoda fabrykująca".

P

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0
somekind napisał(a):

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.

MS
W C++ użyłbym po prostu friendsów do tego. I nie byłoby sprawy. Ale w C# nie ma takiego mechanizmu... - I Bogu dzięki :D
JU
Dlaczego? W jaki inny sposób w C++ poradziłbyś sobie z tym moim problemem? Zostaje tylko klasa wewnątrz innej lub osobna dllka. Albo różne kombinacje templateów z dziedziczeniem i metodami statycznymi.
MS
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 7 godzin
  • Postów:312
1

Wydaje mi się, że chcesz mieć ciastko i zjeść ciastko. Klasa zaprzyjaźniona to dla mnie gwałt na OOP, już bez wdawania się w szczegóły.
Najlepsza odpowiedź już tutaj padła od @somekind

Pod kątem inżynieryjnym najlepsze jest posiadanie dobrego powodu do zrobienia czegoś.

A póki co piszesz:

Zaznaczam, że póki co nie mam żadnego konkretnego problemu, po prostu patrzę dość przyszłościowo

Zastosuj proste rozwiązanie (ale nie prostsze! ;) i czas pokaże, czy było dobre. A jak nie chcesz tworzenia instancji przez new, to zastosuj sobie statyczna analizę, która będzie grzecznie informować, żeby takiej konstrukcji nie używać ;)


edytowany 1x, ostatnio: mstl
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0
mstl napisał(a):

Wydaje mi się, że chcesz mieć ciastko i zjeść ciastko. Klasa zaprzyjaźniona to dla mnie gwałt na OOP, już bez wdawania się w szczegóły.
Najlepsza odpowiedź już tutaj padła od @somekind

Pod kątem inżynieryjnym najlepsze jest posiadanie dobrego powodu do zrobienia czegoś.

A póki co piszesz:

Zaznaczam, że póki co nie mam żadnego konkretnego problemu, po prostu patrzę dość przyszłościowo

Wyrwałeś połowę mojej wypowiedzi. Spójrz na całość:

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.

To jest mój problem.

Zastosuj proste rozwiązanie (ale nie prostsze! ;) i czas pokaże, czy było dobre. A jak nie chcesz tworzenia instancji przez new, to zastosuj sobie statyczna analizę, która będzie grzecznie informować, żeby takiej konstrukcji nie używać ;)

Możesz to rozwinąć?

MS
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 7 godzin
  • Postów:312
1

@Juhas: odnośnie analizy kodu - jeżeli masz R# możesz np. skorzystać z Options -> Code Inspection -> Custom Patterns i możesz dodać sobie, że na new A dostaniesz Warning lub Error. Takie ustawienie później możesz wrzucić do repo. My w jednym z projektów wyświetlamy komunikat, gdy ktoś użyje ViewBag (z ASP.MVC) :D


edytowany 1x, ostatnio: mstl
Zobacz pozostałe 2 komentarze
MS
Niestety nie wiem, czy są darmowe narzędzia, które by to umożliwiały. Ostatnio słyszałem o czymś takim jak Roslynator - https://github.com/JosefPihrt/Roslynator ale nie wiem, jakie ma możliwości jeśli chodzi o analizę.
T9
zawsze można użyć atrybutu np. [Obsolete("Use factory!")]nad konstruktorem i bić po głowie jak ktoś doda nowy warning.
JU
Można, ale to samo wyjdzie przy fabryce :D
T9
To tak to zrób żeby nie wyszło ... np. używając pragma warringw fabryce z resztą masz cały wątek o tym jak tego uniknąć.
JU
No jest to rozwiązanie. Może faktycznie pójdę w tym kierunku.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Wrocław
3
Juhas napisał(a):

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.

Jeśli fabryka ma tworzyć te obiekty, to i tak musi użyć new, więc jeśli nie zadbasz o spójność obiektu w konstruktorze, to równie dobrze coś może się rozjechać w tych obiektach z fabryki.
A źle skonstruowane obiekty, skoro będą złe, to nie będą działać...

Rozumiem, że obiekt jest skomplikowany do stworzenia, więc chcesz dla bezpieczeństwa napisać do niego fabrykę czy tam buldera. Informujesz o tym kolegów z zespołu, i od tej pory używacie tamtych klas, a nie bezpośrednio new. Jak dla mnie po sprawie, jeśli ktoś ręcznie zbuduje zły obiekt, to nie będzie mu działał i nie skończy swojego taska.

Z drugiej strony, jeśli obiekt jest trudny do utworzenia, to może po prostu jest za duży i należałoby go podzielić?

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);

Tworzenie obiektu A, to tworzenie A, a nie umieszczanie go w jakimś innym obiekcie. Jeśli jest taka potrzeba, to powinieneś pisać fabrykę dla jakisInnyObiekt, a nie dla A.

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.

No to zrób fabrykę/buildera i jej używaj. Jeśli ktoś jej nie użyje, a potem spowoduje to problemy w aplikacji, to trudno, i tak łatwiej będzie to naprawić niż wymyślić teraz jakieś rozwiązanie, które poza gmatwaniem kodu będzie się i tak dało obejść.
Oczywiście możesz bawić się w coś takiego:

Kopiuj
class Factory
{
    public Item CreateItem()
    {
        return new Item();
    }
}

class Item
{
    public Item()
    {
        var stackTrace = new StackTrace();
        var methodBase = stackTrace.GetFrame(1).GetMethod();

        if (methodBase.DeclaringType != typeof(Factory))
        {
            throw new ApplicationException("Wracaj do domu łajdaku!");
        }
    }
}

Tylko jakbym ja zobaczył coś takiego w kodzie w pracy, to bym od razu skasował. :P

Najprostszym i chyba najbardziej naturalnym rozwiązaniem wydaje się tu refleksja. I tego bym użył, gdyby refleksja nie była tematem sporów.

Ale co refleksja tu rozwiąże? Przecież każdy może jej użyć i wywołać prywatny konstruktor, dokładnie tak jak Ty w swojej fabryce.

V-2
  • Rejestracja:prawie 8 lat
  • Ostatnio:9 miesięcy
  • Postów:671
2

Uważam, że to zły pomysł.
To próba zmuszenia języka do bycia czymś, czym nie jest.
Gdyby C# miał być tak używany, to by było wspierane na poziomie języka (ten a ten obiekt ma monopol na tworzenie tych obiektów) i nie wymagało symulowania wymyślnymi sztuczkami.
Te sztuczki to jest tzw. evil code, który może być napisany nawet zręcznie, ale jest nieoczywisty.

  1. Miałem jeszcze jakiś pomysł z zagnieżdżaniem fabryki w klasach, ale to od razu eliminuje.

Nie wiem dlaczego, bo spośród rozpatrywanych sposobów ten jest chyba jeszcze najbardziej "przyzwoity", to znaczy szanujący założenia języka i nieprzekombinowany.

Pytanie jest takie: gdy patrzę na ConcreteClassA i nie mogę znaleźć konstruktora, skąd właściwie mam wiedzieć, że mam obowiązek użyć fabryki i gdzie ona jest?
Muszę np. zerknąć do dokumentacji, ewentualnie wydedukować sobie na podstawie lektury zastanego kodu.

A może po prostu popatrzeć na to odwrotnie.
Skoro do odpalenia obiektu nieodzowna jest fabryka, może niech jego konstruktor bierze ową fabrykę jako swój argument.

Kopiuj
public ConcreteClassA(MyClassFactory factory) 
{
	// i tu ustawianie wartości, inicjalizowanie zasobów itd.
}

Oczywiście w takim wypadku to już nie jest wzorzec fabryka i należałoby zmienić tę mylącą nazwę.

Np. na MyClassHydrator (który "nastrzykuje" tworzony obiekt wartościami, strategiami, cokolwiek mu trzeba), albo MyClassInitializer, albo MyClassBootstrapper.

Kopiuj
public ConcreteClassA(MyClassInitializer initializer) 
{
	initializer.initialize(this)
	// hydrator.hydrate(this)
	// bootstrapper.bootstrap(this)
}

Dla każdego kolegi jest wtedy z automatu oczywiste, że to jedyny sposób tworzenia obiektów tej klasy.

I nie trzeba szukać, gdzie ta fabryka w zasadzie jest położona, nie trzeba robić czarcich sztuczek z refleksją, sztucznie wydzieloną dll-ką, rzucać wyjątków w trakcie jazdy, konfigurować ReSharpera i w ogóle "bohatersko walczyć z problemami nieznanymi w normalnych ustrojach"...


Nie ma najmniejszego powodu, aby w CV pisać "email" przed swoim adresem mailowym, "imię i nazwisko" przed imieniem i nazwiskiem, ani "zdjęcie mojej głowy od przedniej strony" obok ewentualnego zdjęcia. W drugiej firmie której już pracuję mam palących marihuanę programistów [...] piszą kod "leniwie", często nie wysilając się, rozwlekając ten kod, unikając np. programowania funkcyjnego (mówię tutaj o lambdach w javie).
edytowany 4x, ostatnio: V-2
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0
somekind napisał(a):

Tworzenie obiektu A, to tworzenie A, a nie umieszczanie go w jakimś innym obiekcie. Jeśli jest taka potrzeba, to powinieneś pisać fabrykę dla jakisInnyObiekt, a nie dla A.

No tak to ma być robione. Fabryka/builder ma być generyczna. Ma posiadać jedną publiczną metodę:

CreateItem<T>

I na podstawie przekazanego typu ma umieć odpowiednio stworzyć obiekt. Obiekty pewnych typów muszą być dodawane do listy, a innych typów nie. Tak jak pisałem - normalnie przekazywałbym tą listę w konstruktorze klasy, ale te klasy mają niczego nie wiedzieć o tym. Poza tym problem jest nieco bardziej skomplikowany niż go przedstawiam. Dlatego też w grę wchodzi tylko builder/fabryka.

Ale co refleksja tu rozwiąże? Przecież każdy może jej użyć i wywołać prywatny konstruktor, dokładnie tak jak Ty w swojej fabryce.

Prywatny konstruktor. Dzięki czemu nie można utworzyć obiektu. Jeśli nie można utworzyć obiektu, to jest już wtedy bardzo duża szansa na to, że gościu przeczyta jednak dokumentację w kodzie przy tym konstruktorze i zorientuje się, że fabryka/builder ma to tworzyć :)
Co sądzicie o takim rozwiązaniu. Ma to sens, czy nie? W tym przypadku narzutem refleksji się w ogóle nie przejmuję.

Ale może faktycznie walczę z wiatrakami i chcę zrobić coś nadgorliwego. Może faktycznie powinienem zostawić to new w spokoju i wpisać w dokumentacji, żeby tak nie tworzyć. Ale wydaje mi się to nie do końca poprawnym rozwiązaniem, bo powoduje wieloznaczność i niespójność aplikacji.

Przeszły mi przez myśl wcześniej rozwiązania ze stack trace i wciskaniem fabryki do konstruktora. Ale jeśli chodzi o stack trace... no to wiadomo ;) A fabryka do konstruktora - cały czas chciałem mieć faktyczną fabrykę/buildera i tu już pojawiał się problem.

Więc czekam jeszcze co myślicie o użyciu refleksji w tym przypadku. I albo zrobię tak, albo po prostu niech te klasy będą wnętrznościami fabryki, albo oleję to new.

V-2
  • Rejestracja:prawie 8 lat
  • Ostatnio:9 miesięcy
  • Postów:671
2
Juhas napisał(a):

Więc czekam jeszcze co myślicie o użyciu refleksji w tym przypadku.

Ja bym nie używał.

Z trzeciej strony ;) skoro dostaliśmy refleksję, fajnie by było czasem jej użyć.

Tak samo tłumaczył chłopiec, któremu na izbie przyjęć trzeba było wyjąć pięciozłotówkę z miejsca, z którego - jak się okazało - trudno ją wyciągnąć samodzielnie.

- Fajnie czasem użyć - zgodził się lekarz dyżurny - ale jednak najpierw trzeba się dwa razy zastanowić.

Ogólna zasada jest taka, że po refleksję sięgamy, kiedy obiekt czy kod, wobec którego stosujemy refleksję, powstaje (jest budowany) później, niż kod stosujący refleksję.

Czyli jeżeli byś np. pisał nowy, najszybszy na świecie serializator do jsonów albo własną bibliotekę ORM - można sięgnąć po refleksję, bo nie możemy wiedzieć z góry, jakie obiekty ktoś nam poda.

Stosowanie jej w obrębie kodu, który jest w całości posiadany i kontrolowany przez nas, to już mocno podejrzane.

Problemy z refleksją są nie tylko wydolnościowe; te można łagodzić. Z samej natury wnosi ona dodatkową warstwę komplikacji - nie tylko dorzuca syfu do śladów stosu, ale też utrudnia działanie różnych narzędzi (np. do statycznej analizy kodu albo obfuskatorów...)


Nie ma najmniejszego powodu, aby w CV pisać "email" przed swoim adresem mailowym, "imię i nazwisko" przed imieniem i nazwiskiem, ani "zdjęcie mojej głowy od przedniej strony" obok ewentualnego zdjęcia. W drugiej firmie której już pracuję mam palących marihuanę programistów [...] piszą kod "leniwie", często nie wysilając się, rozwlekając ten kod, unikając np. programowania funkcyjnego (mówię tutaj o lambdach w javie).
edytowany 1x, ostatnio: V-2
JU
O, o tych dodatkowych problemach nie wiedziałem.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Wrocław
0
Juhas napisał(a):

No tak to ma być robione. Fabryka/builder ma być generyczna. Ma posiadać jedną publiczną metodę:

CreateItem<T>

I na podstawie przekazanego typu ma umieć odpowiednio stworzyć obiekt. Obiekty pewnych typów muszą być dodawane do listy, a innych typów nie.

Generyczność to inaczej uogólnienie. Jeżeli jedne obiekty mają być tworzone inaczej niż drugie, to to jest zaprzeczenie generyczności.

Prywatny konstruktor. Dzięki czemu nie można utworzyć obiektu. Jeśli nie można utworzyć obiektu, to jest już wtedy bardzo duża szansa na to, że gościu przeczyta jednak dokumentację w kodzie przy tym konstruktorze i zorientuje się, że fabryka/builder ma to tworzyć :)

Albo przeczyta i stwierdzi, że ta fabryka mu wcale nie pomaga, i wywoła konstruktor.

Ale może faktycznie walczę z wiatrakami i chcę zrobić coś nadgorliwego. Może faktycznie powinienem zostawić to new w spokoju i wpisać w dokumentacji, żeby tak nie tworzyć. Ale wydaje mi się to nie do końca poprawnym rozwiązaniem, bo powoduje wieloznaczność i niespójność aplikacji.

Nie powoduje, dopóki tego pilnujesz. Jeśli ktoś napisze niepoprawny kod, odrzuć mu go na review.

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0
somekind napisał(a):
Juhas napisał(a):

No tak to ma być robione. Fabryka/builder ma być generyczna. Ma posiadać jedną publiczną metodę:

CreateItem<T>

I na podstawie przekazanego typu ma umieć odpowiednio stworzyć obiekt. Obiekty pewnych typów muszą być dodawane do listy, a innych typów nie.

Generyczność to inaczej uogólnienie. Jeżeli jedne obiekty mają być tworzone inaczej niż drugie, to to jest zaprzeczenie generyczności.

Zgoda, ale posługuję się narzędziami, które mam.

Prywatny konstruktor. Dzięki czemu nie można utworzyć obiektu. Jeśli nie można utworzyć obiektu, to jest już wtedy bardzo duża szansa na to, że gościu przeczyta jednak dokumentację w kodzie przy tym konstruktorze i zorientuje się, że fabryka/builder ma to tworzyć :)

Albo przeczyta i stwierdzi, że ta fabryka mu wcale nie pomaga, i wywoła konstruktor.

No nie wywoła, bo jest prywatny. Więc albo musiałby zmienić konstruktor na publiczny, albo użyć refleksji. W obu przypadkach podejrzewam, że nawet początkujący programista by uznał, że to są złe drogi ;)

Ale może faktycznie walczę z wiatrakami i chcę zrobić coś nadgorliwego. Może faktycznie powinienem zostawić to new w spokoju i wpisać w dokumentacji, żeby tak nie tworzyć. Ale wydaje mi się to nie do końca poprawnym rozwiązaniem, bo powoduje wieloznaczność i niespójność aplikacji.

Nie powoduje, dopóki tego pilnujesz. Jeśli ktoś napisze niepoprawny kod, odrzuć mu go na review.

Review... Piękny byłby to świat... ;)

T9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
  • Postów:329
1

Wiem :D, jeśli twoja fabryka jest singletonem, to możesz ją inicjalizować w statycznych konstruktorach "klas bez new". "klasy bez new" miały by prywatny konstruktor a do fabryki przekazywały by func albo specjalny interfejs.

Kopiuj
class NoNew : NoNewBase
{
    private static object initObject = new NoNew ();
    static NoNew()
    {
           NoNewBase.Factory.Add( (args) => new MoNew(args) );
           initObject = null; //clean up
    }

    //initialize
    private noNew(object args)
    {
         // force static constructor and do nothing
    }
    private noNew(object args)
    {
         // do something
    }
}

Tylko pamiętaj żeby fabrykę zrobić przez if(instance==null)instance=new inst(); return instance i nie polegać na kolejności dodawania elementów bo ona jest nie określona dla konstruktorów statycznych.

edytowany 7x, ostatnio: topik92
JU
Nie widzę tego. Statyczny konstruktor rusza przecież tylko przy pierwszym wywołaniu statycznego membera.
T9
Racja zapomniałem o tym, ale to przecież zaden problem. Można wywołać new na polu statycznym :)
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)