Od pewnego czasu stałem się zagorzałym przeciwnikiem używania metod publicznych. W projektach, w których uczestniczę jak tylko mogę zamieniam public na private i/lub internal i proszę o to samo swoich kolegów. Nie wiem, pewnie jakiś wpływ na to miały książki, które ostatnimi czasy czytałem ale to nie ważne 😀
Wiem, że nie wszystko da się zamknąć w klasie, zrobić z niej “zamknięte pudełko” i na tym zakończyć ale większość oczywiście, że tak.
Na bardzo prostym przykładzie chciałbym wyjaśnić dokładnie o co chodzi.
Przypuśćmy, że mamy 2 klasy. Klasa A i klasa B.
Klasa A implementuje pewne funkcjonalności, tak samo jak klasa B.
Nie trzymamy się tutaj żadego wzorca!
Niech klasa A przetwarza jakieś informacje od użytkownika a klasa B zapisuje te informacje do “czegoś” – plik, baza, nie ważne.
Wychodząc z tego założenia klasa A musi utworzyć sobie obiekt klasy B aby wywołać metodę zapisującą te dane. Najprościej jest stworzyć metodę publiczną w klasie B i ją wywołać prawda?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class A { private string userName; private B b; public A() { b = new B(); } private void GetUserName() { userName = Console.ReadLine(); b.InsertData(userName); } } public class B { public void InsertData(string userName) { //insert userName } } |
A co jeżeli ktoś będzie chciał zrobić nam przysłowiowe kuku i w swoim projekcie dołączy referencję naszej dll’ki, exe’ca?
Ano będzie coś takiego jak na screenie powyżej. Ktoś będzie mógł wywołać naszą metodę InsertData. Ja wiem… zabezpieczamy się na różnych poziomach… logowanie użytkownika, sesje i temu podobne. Ale nie o to mi chodzi. Chodzi mi o to aby ten nieszczęsny public zamienić na private i tu właśnie z pomocą przychodzą nam delegaty.
Nie mam zamiaru rozpisywać się na ich temat bo to nie jest artykuł o nich tylko i wyłącznie. Dla osób, które chcą wiedzieć więcej odsyłam do stron MSDN 🙂
Przytaczając przykład, który opisałem wcześniej postaram się wyjaśnić co można zrobić aby taka sytuacja się nie powtórzyła.
Mając za zadanie napisanie klas(y) i nieumożliwienie osobom postronnym wywołania metod mocno wrażliwych (jak InsertData) pierwsze co powinniśmy zrobić to te właśnie metody zamienić na metody prywatne.
I teraz pewna magia 🙂
Dodajemy delegat
1 |
internal delegate void InsertDataDelegate(string userName); |
internal jest tu użyty celowo.
Wybaczcie mi, ale na temat modyfikatorów dostępu też nie będę się rozpisywał. Zainteresowanych odsyłam do MSDN.
Następnie w klasie, w której mamy naszą metodę insertującą dodajemy metodę, która zwraca nam wcześniej zadeklarowany delegat
1 2 3 4 |
internal InsertDataDelegate GetInsertDataDelegate() { return InsertData; } |
Jak widać metoda zwraca nam tak naprawdę definicję metody InsertData, która wygląda właśnie tak:
1 2 3 4 |
private void InsertData(string userName) { Console.WriteLine(userName); } |
W tym momencie możemy w klasie, która tworzy sobie obiekt klasy zawierającej metodę insertujcą dodać odpowiednie linijki:
1 2 3 |
var dc = new DelegateClass(); InsertDataDelegate insert = dc.GetInsertDataDelegate(); insert("sebastian"); |
W ten oto prosty sposób możemy wywołać metodę prywatną dzięki delegatom.
Prawda, że proste? 🙂
Patrząc teraz na przysłowiowe “kuku”, po podłączeniu referencji naszego projektu do innego, stworzeniu zmiennej naszej klasy intellisense pokaże coś takiego:
Wiem, tak samo jak i Wy, że nie jest to do końca rozwiązanie problemu ponieważ istnieją sposoby na to aby wywołać metody prywatne w inny sposób ale o tym może później.
Tak czy inaczej… jeżeli chcecie chronić swój kod używajcie obfuscator’ów będzie trudniej osobom postronnym zrobić naszej aplikacji “kuku” 😉
Kod do przykładu, do którego dodałem jeszcze inne wariacje wywołania metod – z parametrami, ze zwróceniem wartości znajdziecie na github. Projekt CheckReferenceClassLibrary w solucji jest przykładową “aplikacją”, która posiada referencję do naszego projektu.