Obecnie myślę o interfejsie do klasy, którą piszę. Ta klasa zawiera style dla znaku, na przykład czy znak jest pogrubiony, pochylony, podkreślony itp. Przez dwa dni debatowałem ze sobą, czy powinienem używać metod pobierających / ustawiających lub nazw logicznych dla metod, które zmieniają wartości na te style. Chociaż wolę nazwy logiczne, oznacza to pisanie kodu, który nie jest tak wydajny i nie tak logiczny. Dam ci przykład.
Mam klasy CharacterStyles
, która ma zmienne składowe bold
, italic
, underline
(i kilka innych, ale zostawię je zachować to proste). Najprostszym sposobem, aby umożliwić innym częściom programu dostęp do tych zmiennych, byłoby napisanie metod pobierających / ustawiających, abyś mógł to zrobić styles.setBold(true)
i styles.setItalic(false)
.
Ale mi się to nie podoba. Nie tylko dlatego, że wiele osób twierdzi, że osoby pobierające / ustawiające przerywają enkapsulację (czy to naprawdę tak źle?), Ale przede wszystkim dlatego, że nie wydaje mi się to logiczne. Spodziewam się stylizować znak za pomocą jednej metody styles.format("bold", true)
lub czegoś podobnego, ale nie za pomocą wszystkich tych metod.
Jest jednak jeden problem. Ponieważ nie możesz uzyskać dostępu do zmiennej elementu obiektu przez zawartość łańcucha w C ++, musiałbym albo napisać duży kontener if-instrukcja / switch dla wszystkich stylów, albo musiałbym przechowywać style w tablicy asocjacyjnej ( mapa).
Nie mogę ustalić, jaki jest najlepszy sposób. W jednej chwili myślę, że powinienem napisać getters / setters, a za chwilę pochylam się w drugą stronę. Moje pytanie brzmi: co byś zrobił? A dlaczego miałbyś to zrobić?
bold
ustawionym na,true
a inne zmienne są niezdefiniowane, metody pobierające dla innych zmiennych powinny zwracać wartość stylu nadrzędnego (który jest przechowywany w innej właściwości), podczas gdy moduł pobierający dlabold
właściwości powinien zwracaćtrue
. Istnieją również inne rzeczy, takie jak nazwa i sposób wyświetlania jej w interfejsie.Odpowiedzi:
Tak, pobierający / ustawiający przerywają enkapsulację - w zasadzie są tylko dodatkową warstwą pomiędzy bezpośrednim dostępem do podstawowego pola. Równie dobrze możesz po prostu uzyskać do niego bezpośredni dostęp.
Teraz, jeśli chcesz uzyskać bardziej złożone metody dostępu do pola, jest to poprawne, ale zamiast ujawniać pole, musisz pomyśleć, jakie metody powinna oferować klasa. to znaczy. zamiast klasy Bank z wartością Pieniądze, która jest ujawniana przy użyciu nieruchomości, musisz pomyśleć, jaki rodzaj dostępu powinien oferować obiekt Banku (dodać pieniądze, wypłacić, uzyskać równowagę) i wdrożyć je. Właściwość zmiennej Money różni się tylko składniowo od bezpośredniego wyświetlania zmiennej Money.
DrDobbs ma artykuł, który mówi więcej.
W przypadku twojego problemu mam 2 metody setStyle i clearStyle (lub cokolwiek innego), które przyjmują wyliczenie możliwych stylów. Instrukcja switch wewnątrz tych metod zastosowałaby następnie odpowiednie wartości do odpowiednich zmiennych klas. W ten sposób możesz zmienić wewnętrzną reprezentację stylów na coś innego, jeśli później zdecydujesz się zapisać je jako ciąg znaków (na przykład w HTML) - coś, co wymagałoby zmiany wszystkich użytkowników twojej klasy, jeśli używałeś pobierz / ustaw właściwości.
Nadal możesz włączyć ciągi, jeśli chcesz przyjmować dowolne wartości, albo mieć dużą instrukcję if-then (jeśli jest ich kilka), albo mapę wartości ciągów do wskaźników metod przy użyciu std :: mem_fun (lub std :: funkcja ), więc „pogrubienie” byłoby przechowywane w kluczu mapy, a jego wartość to sts :: mem_fun dla metody, która ustawia zmienną pogrubioną na true (jeśli ciągi znaków są takie same jak nazwy zmiennych składowych, można również użyć stringifying makro , aby zmniejszyć ilość kodu trzeba napisać)
źródło
styles.setStyle(BOLD, true)
? Czy to jest poprawne?styles.getStyle(BOLD)
gdy masz nie tylko zmienne składowe typu boolean, ale także liczby całkowite i ciąg znaków (na przykładstyles.getStyle(FONTSIZE)
:). Ponieważ nie możesz przeciążać typów zwrotów, jak to zaprogramujesz? Wiem, że możesz użyć pustych wskaźników, ale dla mnie to brzmi naprawdę źle. Czy masz jakieś wskazówki, jak to zrobić?Jednym z pomysłów, których mogłeś nie wziąć pod uwagę, jest Wzór Dekoratora . Zamiast ustawiać flagi w obiekcie, a następnie stosować te flagi do wszystkiego, co piszesz, zawijasz klasę, która zajmuje się pisaniem w dekoratorach, które z kolei stosują style.
Kod wywołujący nie musi wiedzieć, ile z tych opakowań otoczył tekst, wystarczy wywołać metodę na zewnętrznym obiekcie i wywołuje stos.
Na przykład pseudokodu:
I tak dalej, dla każdego stylu dekoracji. W najprostszym użyciu możesz powiedzieć
A kod wywołujący tę metodę nie musi znać szczegółów tego, co otrzymuje. Tak długo, jak COŚ jest w stanie narysować tekst za pomocą metody WriteString.
źródło
TextWriter
potrzebować porozmawiać z nimiBoldDecorator
iItalicDecorator
(dwukierunkowo), aby wykonać zadanie.Skłoniłbym się do drugiego rozwiązania. Wygląda bardziej elegancko i elastycznie. Chociaż trudno jest wyobrazić sobie inne typy niż pogrubienie, kursywa i podkreślenie (overline?), Trudno byłoby dodać nowe typy przy użyciu zmiennych składowych.
Zastosowałem to podejście w jednym z moich najnowszych projektów. Mam klasę, która może mieć wiele atrybutów boolowskich. Liczba i nazwy atrybutów mogą się zmieniać w czasie. Przechowuję je w słowniku. Jeśli atrybut nie jest obecny, zakładam, że jego wartość to „fałsz”. Muszę też przechowywać listę dostępnych nazw atrybutów, ale to już inna historia.
źródło
Myślę, że przesadnie zastanawiasz się nad tym problemem.
styles.setBold(true)
istyles.setItalic(false)
są OKźródło