Refaktoryzacja - zastąpienie instrukcji if/switch polimorfizmem - potrzebna pomoc

Refaktoryzacja - zastąpienie instrukcji if/switch polimorfizmem - potrzebna pomoc
VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Bk
  • Postów:480
0

Cześć, przychodzę z prośbą o nakierowanie mnie na właściwe rozwiązanie.
(Nie jestem zawodowym programistą)
(PS2. Pisałem na forum C# i .NET ale tam nie bylo odzewu wiec ponawiam tutaj.)

Opis funkcjonalności programu który już jest i działa.
Program jest wykorzystywany w magazynie podczas pakowania wyrobów w pudełka jednostkowe. Praca polega na włożeniu wyrobu do pudelka, sczytaniu z wyrobu numeru seryjnego czytnikiem kodów kreskowych, program na podstawie odczytu czytnikiem drukuje etykietę z tym numerem i etykietę tą pracownik magazynu nakleja na zamknięte pudełko. To tak bardzo w skrócie. Dodatkowo zrobiłem to tak ze program jest obsługiwany tylko czytnikiem kodów kreskowych, praktycznie nie wymaga podchodzenia do klawiatury ani myszki. Czyli po uruchomieniu programu kursor jest na jednym TextBox'ie i zależnie jaki typ odczytu zostanie mu wczytany program zachowa się inaczej.
Typu "rozkazów":

  1. odczyt nr zlecenia - program pobiera z bazy danych artykuł tego zlecenia i otwiera okno dla kierownika gdzie weryfikowane są dane czy wszystko jest ok, jeżeli ok to kierownik zatwierdza i magazynier może używać programu do pakowania tego zlecenia (kierownik używa klawiatury w swoim oknie )
  2. odczyt nr seryjnego - program pobiera nr seryjny i drukowana jest etykieta na Zebrze z nr seryjnym, nazwa wyrobu i grafikami wcześniej pobranymi dla tego artykułu z odczytu nr zlecenia. Dodatkowo co odczyt jest pobierany czas wydruku i na podstawie tego rejestrowany jest czas pakowania każdego z wyrobów i łącznie całego zlecenia.
  3. odczyt "Raport" - program drukuje etykietę na której jest mały raport dla magazyniera do rozliczenia swojej pracy, co pakował, kiedy, ile zapakował i w jakim czasie łącznie z średnim czasem pakowania na sztukę.
  4. odczyt "Czyść" - czyści stan programu i przywraca do ustawień początkowych gdzie należy znów odczytać nr zlecenia żeby ustawić program do pakowania nowych wyrobów.

Obecnie dekoder typów odczytów/rozkazów jest na "if'ach" a sama logika interpretacji odczytu na jednym dużym switchu który w swoich case'ach ma kolejne switche - przykład na samym dole.

Chciałbym z odczytu/rozkazu zrobić obiekt który będzie implementował jakiś interfejs z np. metodą Wykonaj(). Wtedy łatwiej będę mógł dodawać nowe typy etykiet i nowe typy rozkazów i teraz pytanie:
Jak to ogarnąć?

  1. Wzorzec FabrykaAbstrakcyjna jest względnie zrozumiały dla mnie i chce iść w tym kierunku - czy to dobry kierunek? potrzebuje rady kogoś doświadczonego.
  2. Jak maja wyglądać te obiekty "Odczyt/Rozkaz" to chyba nie będą jakieś proste encje, wyobrażam sobie ze dla odczytów typu "Raport" będę musiał wstrzyknąć mój obiekt który odpowiada za rejestracje czasów i generowanie statystyk pakowania, dla rozkazu "Czyść" powinienem wstrzyknąć obiekt "PresenteraOknaGlownego" za pomocą którego wyczyszczę stan całej aplikacji i zresetuje widok aplikacji, za pomocą odczytu "nrZlecenia" będę musiał powstrzykiwac chyba repozytoria z danymi z baz danych a w obiekcie odczytu "nrSeryjny" chyba musze wstrzyknąć obiekt odpowiedzialny za wydruki etykiet (on już będzie musiał zapanować nad tym co to za etykieta i z jaka treścią - wiec tu chyba tez wzorzec fabryka abstrakcyjna?)

Program napisałem na bazie swojego starego programu sprzed prawie 10lat jeszcze z innej firmy w WinForms pod zdarzeniami. Teraz przerabiam go na architekturę MVP, względnie dobrze idzie - stopniowo. Spod zdarzeń przenoszę kod do wydzielonych obiektów, widok już całkowicie odciąłem od logiki. Teraz mam problem z tym kawalkiem architektury logiki - jakos obawiam sie ze te obiekty "odczyty" to będą kobyły czy to dobrze?

dekoder odczytow czytnika obecnie

Kopiuj
    public class DekoderOdczytowCzytnika
    {
        string regexUID = @"100\d{7}\b";
        string regexNrZlecenia = @"501[0-9]{7}\b";

        public TypRozkazuEnum RozpoznajOdczytCzytnika(string odczyt)
        {
            TypRozkazuEnum wynik = TypRozkazuEnum.NiezdefiniowanyTekst;

            if (odczyt == "Raport")
                wynik = TypRozkazuEnum.Raport;
            else if (odczyt == "Czysc")
                wynik = TypRozkazuEnum.Czysc;
            else if (odczyt == "WalidatorProg")
                wynik = TypRozkazuEnum.WalidatorProg;
            else if (Regex.IsMatch(odczyt, regexUID))
                wynik = TypRozkazuEnum.OdczytUID;
            else if (Regex.IsMatch(odczyt, regexNrZlecenia))
                wynik = TypRozkazuEnum.OdczytNrZlecenia;
            else
                wynik = TypRozkazuEnum.NiezdefiniowanyTekst;

            return wynik;
        }
    }

i fragment obiektu odpowiedzialnego za interpretacje odczytow i wydruki - tez obecnie

Kopiuj
    public class KontrolerDrukowania
    {
        private string nazwaDrukarki;
        private DekoderOdczytowCzytnika dekoderOdczytow;
        private RejestratorCzasu rejestratorCzasu;
        private PresenterOknaGlownego presenterOknaGlownego;

        public KontrolerDrukowania(PresenterOknaGlownego presenter)
        {
            this.presenterOknaGlownego = presenter;
            dekoderOdczytow = new DekoderOdczytowCzytnika();
            rejestratorCzasu = new RejestratorCzasu();
        }

//(...........)
public void DrukujEtykiete(TypEtykiety parametryWybranejEtykiety, 
                                    string txtB_NazwaKlienta, 
                                    string txtB_UID, 
                                    string txtB_WersjaProg)
        {
            if (txtB_NazwaKlienta != null
                && txtB_UID != null
                && txtB_NazwaKlienta != "Nazwa_Klienta"
                && txtB_UID != "UID"
                && txtB_WersjaProg != "Wersja_Programu")
            {
                IEtykietaDrukowalna etykietaDoWydruku = null;

                switch (dekoderOdczytow.RozpoznajOdczytCzytnika(txtB_UID))
                {
                    case TypRozkazuEnum.Raport:
                        switch (parametryWybranejEtykiety.RozmiarPapieruEtykiety)
                        {
                            case RozmiarPapieruEtykietyEnum.Rozmiar82x56:
                                etykietaDoWydruku = new EtykietaRaport_82x56(
                                                                txtB_NazwaKlienta,
                                                                rejestratorCzasu.ZwrocTekstem_CalkowityCzasPakowania(),
                                                                rejestratorCzasu.ZwrocTekstem_SredniCzasPakowania());
                                break;
                            case RozmiarPapieruEtykietyEnum.Rozmiar40x20:
                                Informator.PokazBlad(
                                    "Nie można obecnie drukowac raportu na małej etykiecie",
                                    "Za mała etykietka na raport");
                                break;
                            default:
                                Informator.PokazBlad(
                                    "Niezdefiniowany rozmiar papieru etkiety",
                                    "Blad wyboru etykiety");
                                break;
                        }
                        presenterOknaGlownego.WyczyscPoleCzytnikaUID();                     
                        break;

                    case TypRozkazuEnum.Czysc:
                        rejestratorCzasu.WyczyscRejestrOdczytow();
                        presenterOknaGlownego.ZresetujWidok();

                        Informator.PokazInformacje("Wyczyszczenie danych historii wydrukow i czasow pakowania", "Zerowanie aplikacji");
                        break;

                    case TypRozkazuEnum.OdczytUID:
                        switch (parametryWybranejEtykiety.RozmiarPapieruEtykiety)
                        {
                            case RozmiarPapieruEtykietyEnum.Rozmiar82x56:
                                etykietaDoWydruku = new EtykietaWydruk_82x56(
                                                                txtB_UID,
                                                                txtB_NazwaKlienta,
                                                                txtB_WersjaProg);
                                break;
                            case RozmiarPapieruEtykietyEnum.Rozmiar40x20:
                                etykietaDoWydruku = new EtykietaWydruk_40x20(
                                                                txtB_UID);
                                break;
                            default:
                                Informator.PokazBlad(
                                    "Niezdefiniowany rozmiar papieru etkiety",
                                    "Blad wyboru etykiety");
                                break;
                        }

                        rejestratorCzasu.DodajOdczyt(new CzasOdczytuUID(txtB_UID, DateTime.Now));
                        presenterOknaGlownego.WyczyscPoleCzytnikaUID();
                        presenterOknaGlownego.WyswietlPodsumowanie(rejestratorCzasu.ZwrocIloscZarejestrowanychOdczytow().ToString() + " [szt.]");
                        break;

                    default:
                        //TODO tutaj zamiast wydruku dodac wyswietlenie messageBox
                        etykietaDoWydruku = new EtykietaBledu();
                        presenterOknaGlownego.WyczyscPoleCzytnikaUID();        
                        break;
                }

                if (etykietaDoWydruku != null)
                {
                    //zewnetrzna biblioteka do drukarek Zebra
                    RawPrinterHelper.SendStringToPrinter(
                        NazwaPrzypisanejDrukarkiZebra,
                        etykietaDoWydruku.ZwrocKodEtykietyZPL());
                }
            }
            else
            {
                Informator.PokazBlad("Któreś z poniższych pól zostało niewłaściwie wypełnione:" + Environment.NewLine +
                    "- 'Nazwa Klienta'" + Environment.NewLine +
                    "- 'Wersja Programu'" + Environment.NewLine +
                    "- 'pole ID' ?", "Źle wypełnione pola");
            }
        }


Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
elwis
  • Rejestracja:ponad 18 lat
  • Ostatnio:10 dni
2

Na twoim miejscu użyłbym tablicy akcji (dispatch table). Innymi słowy deklarujesz sobie słownik, w której kluczem jest regex określający komendę, a w wartości funkcję, która opisuje dane zdarzenie. Właściwie to bym zrobił tylko jeden regex parsujący linijkę (albo po prostu split) i wybierał akcję na podstawie stringa. Tak byłoby szybciej, bo w przypadku regexa musisz wszystkie po kolei sprawdzać, a przez stringa to jest O(1), czyli czas wykonania nie zależy od ilości opcji.

Przecież to są tylko akcje, które nie mają swojego stanu (chyba, że coś przeoczyłem), więc nie ma potrzeby tworzyć klas, a już w ogóle Fabryka Abstrakcyjna to przegięcie (pomijając to, że dla mnie to jest antywzorzec).


edytowany 5x, ostatnio: elwis
VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Bk
  • Postów:480
0
  1. czyli taki obiekt ze słownikiem [klucz odczytu , delegat do metody innych obektow] miałby nazwę np. KontrolerOdczytow i byłby tworzony w prezenterze okna głównego?

  2. co do tego czy aplikacja ma stany, z tym tez mam problem z interpretacja, bo z jednej strony jest konieczna kolejność uruchamiania aplikacji (uruchomienie -> odczyt zlecenia -> walidacja poprawności -> normalne pakowanie ->raport lub czyszczenie) z każdego z tych stanów może być pare dopuszczalnych wyjść ale tez zabronione np. z normalnego pakowania mogę wejść w raport i z raportu albo na czyszczenie albo na dalszy ciąg normlanego pakowania. Chyba ze źle identyfikuje stany aplikacji i to załatwię tylko tym słownikiem [klucz odczytu , delegat do akcji]

Ps. I jeszcze jedno mnie interesuje w tej apce mam dwa cele.

  1. Krytyczny. To zferaktoryzowac ją do zjadliwej postaci zgodnej z wzorcem MVP. I oddać do użytku w formie gdzie łatwo będę mógł dodawać nowe etykiety i nowe rozkazy.
  2. Niekrytyczny. Zferaktoryzowac to możliwie najwygodniej pod refaktoryzacja WPF MVVM, to działanie traktuje jako wprawkę.

PS3. Wydaje się że filmik z wczoraj na YouTube mówi to samo co Ty @elwis , podłubię w tą stronę i jakby co będę podpytywał o poprawność architektury dalej


Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
edytowany 4x, ostatnio: VarrComodoo
elwis
  • Rejestracja:ponad 18 lat
  • Ostatnio:10 dni
1
Varran napisał(a):
  1. czyli taki obiekt ze słownikiem [klucz odczytu , delegat do metody innych obektow] miałby nazwę np. KontrolerOdczytow i byłby tworzony w prezenterze okna głównego?

Nie jestem pewny czy byłby to delegat, bo nie programuję w C#. Jeśli jest to to samo co referencja na funkcję to pewnie koniec końców by się do tego sprowadziło, ale na poziomie kodu, jeśli to możliwe, warto użyć wyrażeń lambda, bo wtedy powstaje nam ładny czytelny kod i nie trzeba szukać funkcji. Niestety nie wiem jak to zapisać w C# (o ile można, np. w Javie trzeba to robić bardzo na około), ale idealnie jeśli możesz napisać coś podobnego do tego:

Kopiuj
my %actions = (
    dodaj => sub {
       #akcja dodaj…
    }, odejmij => sub {
       # akcja odejmij…
    } …
);

#… użycie.

my $line = readline(STDIN);
my @args = split (/\S+/, $line);
$actions{$args[0]}->();
  1. co do tego czy aplikacja ma stany, z tym tez mam problem z interpretacja, bo z jednej strony jest konieczna kolejność uruchamiania aplikacji (uruchomienie -> odczyt zlecenia -> walidacja poprawności -> normalne pakowanie ->raport lub czyszczenie) z każdego z tych stanów może być pare dopuszczalnych wyjść ale tez zabronione np. z normalnego pakowania mogę wejść w raport i z raportu albo na czyszczenie albo na dalszy ciąg normlanego pakowania. Chyba ze źle identyfikuje stany aplikacji i to załatwię tylko tym słownikiem [klucz odczytu , delegat do akcji]

To, że aplikacja ma stan to nie ulega wątpliwości, bo każda aplikacja okienkowa ma jakiś stan. Chodzi mi o to czy akcje — tak jak bloki case mają stan. Z tego co widzę, stan trzymasz w obiektach, które trzymasz sobie gdzieś nazewnątrz, więc same akcje nie muszą utrzymywać swojego lokalnego stanu. Na przykład w takim przypadku miałbyś akcję ze stanem:

Kopiuj
   nastepny_id => sub {
        state $last = 0;
        return $last++;
   }

czyli mamy akcję, która zwróci następną liczbę.

PS3. Wydaje się że filmik z wczoraj na YouTube mówi to samo co Ty @elwis , podłubię w tą stronę i jakby co będę podpytywał o poprawność architektury dalej

To są jeszcze inne sposoby na uniknięcie if i else. Wydaje mi się, że wzorzec Strategy jest najbliższy temu, choć teoretycznie jest do czegoś innego.


edytowany 3x, ostatnio: elwis
KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
5

Zacznijmy od tego: po co chcesz to refaktorować? Jaki problem z kodem chcesz rozwiązać?
Dopóki nie będziesz mieć twardego merytorycznego uzasadnienia, to się za to nie bierz.

"Słyszałem, ze switche są złe" to nie jest powód a jedynie jakaś tam opinia, nie mająca żadnej wartości bez kontekstu.

Co do samego kodu - kod jest trochę rozwlekły i należałoby go najpierw podzielić na mniejsze kawałki - bo teraz jest pomieszanie różnych poziomów abstrakcji w jednej metodzie. Ale co do zastępowania switcha polimorfizmem (zapewne wzorzec Command tu by najbardziej pasował) to już miałbym wątpliwości, bo to zależy od tego co chcesz osiągnąć i w którą stronę planujesz ten kod potem rozwijać.

VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Bk
  • Postów:480
0

Refaktoryzuje go żeby łatwiej moc później dodawać nowe etykiety i rozkazy sterujące programem.


Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
elwis
  • Rejestracja:ponad 18 lat
  • Ostatnio:10 dni
1
Krolik napisał(a):

"Słyszałem, ze switche są złe" to nie jest powód a jedynie jakaś tam opinia, nie mająca żadnej wartości bez kontekstu.

@Varran przecież przyznał, że nie jest programuje zawodowo, Takie coś można powiedzieć seniorowi, być może mocnemu midowi, ale początkujący czy hobbysta musi się opierać na opiniach bardziej doświadczonych. :) Taka osoba przecież na ogół nawet nie wie, czym utrudnia sobie życie, mi w każdym razie zajęło kilka lat pracy zawowodowej, żeby to zacząć ogarniać.

Switch akurat jest zły z trzech względów. Po pierwsze, ze względu na breaki, które mogą się zgubić, po drugie, wszystkie przpadki stanowią jeden blok. Dodatkowo, duży switch powoduje, że nie są wydzielane funkcje, które powinny być wydzielone. Switch to jest relikt sprzed kilku dekad, nie mam pojęcia po co istnieje we współczesnych językach programowania, bo znacznie lepiej zrobić tablicę akcji lub użyć dziedziczenia. W tym akurat przypadku nie ma się nad czym rozowodzić.

Odnośnie refactoru to polecam bardzo dobrą heurystykę, której uczyli nas na pierwszym roku studiów: funkcje (czy metody, na jedno wychodzi) powinny być krótkie. Jeden ekran (~30 linii) to jest max, a najlepiej, jeśli mają nie więcej niż 10. Ta zasada się sprawdza, bo im jest dłuższa tym później trudniej dojść co właściwie robi, łatwiej też przeoczyć jakiś szczegół. Plus, funkcja ma swoją nazwę (lub klucz w przypadku tablicy akcji), która dodatkowo ją dokumentuje. W przypadku przytoczonego kawałka kodu, wydzielenie poszczególnych przypadków ze switcha, zdaje się, wystarczy.


S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
2

Switch akurat jest zły z trzech względów. Po pierwsze, ze względu na breaki, które mogą się zgubić, po drugie, wszystkie przpadki stanowią jeden blok. Dodatkowo, duży switch powoduje, że nie są wydzielane funkcje, które powinny być wydzielone. Switch to jest relikt sprzed kilku dekad, nie mam pojęcia po co istnieje we współczesnych językach programowania, bo znacznie lepiej zrobić tablicę akcji lub użyć dziedziczenia. W tym akurat przypadku nie ma się nad czym rozowodzić.

Switch taki jak jest w Javie 17 czy w Kotlinie jest jak najbardziej OK w odpowiednich zastosowaniach, czyli mamy datę i robimy logikę która obsługuje osobno pon-piątek, sobotę i niedzielę, czy też robimy pattern matchich. Ważne żeby to był expression a nie statement i wymuszał obsługę wszystkich przypadków ( @jarekr000000 pewnie wie jak to się mądrze nazywa).


stivens
wymuszał obsługę wszystkich przypadków - exhaustive
S9
O właśnie
VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Bk
  • Postów:480
0

mam trochę problem z tym słownikiem poleceń, to jak to teraz próbuje, poniżej:

wątpliwości i pytania:

  1. ten obiekt dyspozytorPoleceń musiałby mieć referencje - ustawionych już w konstruktorze - do wszystkich obiektów w sobie żeby przełożyć do niego kod ze switchy (przeklejając kod ze switchy jeden do jednego) to dziwne
  2. wolałbym przekazywać referencje do obiektów w konkretnych metodach obsługi polecenia, ale wtedy jak przekazywać do tych metod parametry jeżeli wywołuje je przez Invoke()?
  3. przykład rozkazu "Raport" i "OdczytUID" - oba przygotowują etykiety do druku ale na podstawie parametru podanego w switchu "parametryWybranejEtykiety.RozmiarPapieruEtykiety" i wtedy tworzona jest właściwa etykieta, czy tu ma być taki słownik poleceń zagnieżdżony? tak jak switche są zagnieżdżone?
Kopiuj
    public class DyspozytorPolecen
    {
        private Dictionary<TypRozkazuEnum, Action> slownikPolecen;

        private DekoderOdczytowCzytnika dekoderOdczytow;
        private RejestratorCzasu rejestratorCzasu;
        private PresenterOknaGlownego presenterOknaGlownego;

        public DyspozytorPolecen(PresenterOknaGlownego presenter)
        {
            this.presenterOknaGlownego = presenter;
            dekoderOdczytow = new DekoderOdczytowCzytnika();
            rejestratorCzasu = new RejestratorCzasu();

            slownikPolecen.Add(TypRozkazuEnum.Raport, ObsluzRaport);
            slownikPolecen.Add(TypRozkazuEnum.Czysc, ObsluzCzyszczenie);
            slownikPolecen.Add(TypRozkazuEnum.OdczytUID, ObsluzOdczytUID);
            slownikPolecen.Add(TypRozkazuEnum.OdczytNrZlecenia, ObsluzOdczytNrZlecenia);
            slownikPolecen.Add(TypRozkazuEnum.WalidatorProg, ObsluzWalidatoraProgramu);
        }

        public void WykonajPolecenieDla(TypRozkazuEnum rozkazEnum)
        {
            slownikPolecen[rozkazEnum]?.Invoke();
        }

        private void ObsluzRaport() { }
        private void ObsluzCzyszczenie() 
        {
            rejestratorCzasu.WyczyscRejestrOdczytow();
            presenterOknaGlownego.ZresetujWidok();
            Informator.PokazInformacje("Wyczyszczenie danych historii wydrukow i czasow pakowania", "Zerowanie aplikacji");
        }
        private void ObsluzOdczytUID() { }
        private void ObsluzOdczytNrZlecenia() { }
        private void ObsluzWalidatoraProgramu() { }
    }

Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
edytowany 2x, ostatnio: VarrComodoo
Miang
  • Rejestracja:prawie 7 lat
  • Ostatnio:35 minut
  • Postów:1659
3
elwis napisał(a):

@Varran przecież przyznał, że nie jest programuje zawodowo, Takie coś można powiedzieć seniorowi, być może mocnemu midowi, ale początkujący czy hobbysta musi się opierać na opiniach bardziej doświadczonych. :) Taka osoba przecież na ogół nawet nie wie, czym utrudnia sobie życie, mi w każdym razie zajęło kilka lat pracy zawowodowej, żeby to zacząć ogarniać.

Przecież @Krolik bardzo ważną rzecz napisał, o podzieleniu kodu na mniejsze kawałki i od tego początkujący powinien zacząć


dzisiaj programiści uwielbiają przepisywać kod z jednego języka do drugiego, tylko po to by z projektem nadal stać w miejscu ale na nowej technologii
elwis
Zanegowanie części wypowiedzi nie neguje jej reszty. :)
VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Bk
  • Postów:480
0

drugie co wymyśliłem to takie cos, ale tez mam wrażenie że to jakieś dziwne ładowanie wszystkiego do obiektów obsługi:

taka tablica przypisan ale nie oakcji tylko obiektow ktore reprezentuja jakis dzialania;

Kopiuj
    public class DyspozytorPolecen
    {
        private Dictionary<TypRozkazuEnum, IPolecenie> slownikObiektowPolecen;

        public IEtykietaDrukowalna WykonajPolecenieDla(TypRozkazuEnum rozkazEnum, string odczyt)
        {
            return slownikObiektowPolecen[rozkazEnum]?.WykonajDzialanie(odczyt);
        }

        public void ZdefiniujObsluge(TypRozkazuEnum rozkaz, IPolecenie obiektPolecenia)
        {
            slownikObiektowPolecen.Add(rozkaz, obiektPolecenia);
        }
    }

interfejsy po których będę wykonywa akcje lub zwracał właściwa etykietę.

Kopiuj
public interface  IPolecenie
    {
        IEtykietaDrukowalna WykonajDzialanie(string odczyt);
    }

i przykładowy obiekt obsługi jednego typu polecenia:

Kopiuj
    public abstract class ObslugaBase
    {
        protected IEtykietaDrukowalna etykietaDoWydruku = null;
        protected TypEtykiety parametryEtykiety 
        { 
            get { return presenterOknaGlownego.TypWybranejEtykiety; }
            set { presenterOknaGlownego.TypWybranejEtykiety = value; }
        }
        protected DaneArtykuluProgramowalnego pakowanyArtykul 
        { 
            get { return presenterOknaGlownego.wybranyDoPakowaniaArtykul; }
            set { presenterOknaGlownego.wybranyDoPakowaniaArtykul = value; }
        }

        protected RejestratorCzasu rejestratorCzasu;
        protected PresenterOknaGlownego presenterOknaGlownego;

        public ObslugaBase(PresenterOknaGlownego presenter, RejestratorCzasu rejestrator)
        {
            rejestratorCzasu = rejestrator;
            presenterOknaGlownego = presenter;
        }
    }

public class ObslugaWydrukuRaportu : ObslugaBase, IPolecenie
    { 
        public ObslugaWydrukuRaportu(PresenterOknaGlownego presenter, RejestratorCzasu rejestrator)
            : base(presenter, rejestrator) { }

        public IEtykietaDrukowalna WykonajDzialanie(string odczyt)
        {
            switch (parametryEtykiety.RozmiarPapieruEtykiety)
            {
                case RozmiarPapieruEtykietyEnum.Rozmiar82x56:
                    etykietaDoWydruku = new EtykietaRaport_82x56(
                                                    pakowanyArtykul.NazwaKlienta,
                                                    rejestratorCzasu.ZwrocTekstem_CalkowityCzasPakowania(),
                                                    rejestratorCzasu.ZwrocTekstem_SredniCzasPakowania());
                    break;
                case RozmiarPapieruEtykietyEnum.Rozmiar40x20:
                    Informator.PokazBlad(
                        "Nie można obecnie drukowac raportu na małej etykiecie",
                        "Za mała etykietka na raport");
                    break;
                default:
                    Informator.PokazBlad(
                        "Niezdefiniowany rozmiar papieru etkiety",
                        "Blad wyboru etykiety");
                    break;
            }
            presenterOknaGlownego.UstawPoleCzytnikaUID(String.Empty);

            return etykietaDoWydruku;
        }
    }

Ustawianie obiektu z przypasowaniami typów rozkazów do konkretnych obiektów poleceń

Kopiuj
        public EgzekutorPolecen(PresenterOknaGlownego presenter)
        {
            this.presenterOknaGlownego = presenter;
            weryfikatorZebry = new WeryfikatorDrukarkiZebra();

            dekoderOdczytow = new DekoderOdczytowCzytnika();
            rejestratorCzasu = new RejestratorCzasu();

            dyspozytor = new ZarzadcaPolecen();
            dyspozytor.ZdefiniujObsluge(TypRozkazuEnum.Raport, new ObslugaWydrukuRaportu(presenterOknaGlownego, rejestratorCzasu));
            dyspozytor.ZdefiniujObsluge(TypRozkazuEnum.Czysc, new ObslugaCzyszczenia(presenterOknaGlownego, rejestratorCzasu));
            dyspozytor.ZdefiniujObsluge(TypRozkazuEnum.OdczytUID, new ObslugaDrukowaniaEtykietyUID(presenterOknaGlownego, rejestratorCzasu));
        }

.

Nowa metoda zamiast switch'ów byłaby taka:

Kopiuj
public void DrukujEtykiete(DaneArtykuluProgramowalnego artykul, string odczytWartosc)
        {
            TypRozkazuEnum rozkaz = dekoderOdczytow.RozpoznajOdczytCzytnika(odczytWartosc);
            IEtykietaDrukowalna etykietaDoWydruku = dyspozytor.WykonajPolecenieDla(rozkaz, odczytWartosc);

            if (etykietaDoWydruku != null)
            {
                Informator.PokazInformacje(
                "Poszedl Wydruk: " + Environment.NewLine +
                etykietaDoWydruku.ZwrocKodEtykietyZPL(),
                "Info o wydruku etykiety");
                //RawPrinterHelper.SendStringToPrinter(
                //    NazwaWybranejZebry,
                //    etykietaDoWydruku.ZwrocKodEtykietyZPL());
            }
        }

zakręciłem się w tym? czy to dobry kierunek?


Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
edytowany 5x, ostatnio: VarrComodoo
VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Bk
  • Postów:480
0

Rozpędziłem się z tymi obiektami implementującymi interfejs "IPolecenie" a teraz coraz więcej poleceń mi dochodzi, które nie powinny zwracać żadnej etykiety. Są to polecenia zmieniające stan aplikacji. Przykład resetowania stanu aplikacji poniżej:

Jak poprawić ten kod aby móc wykonywać dwa rodzaje poleceń: jedno zwracające wydruk i drugie tylko systemowe zwracające void. Czy musze robić to dwoma "EgzekutoramiPolecen"?

Kopiuj
 public IEtykietaDrukowalna WykonajDzialanie(string odczyt)
        {
            rejestratorCzasu.WyczyscRejestrOdczytow();
            presenterOknaGlownego.UstawWidokStartowyAplikacji();
            parametryEtykiety = new TypEtykiety();
            pakowanyArtykul = new DaneArtykuluProgramowalnego();
           
            Informator.PokazInformacje("Wyczyszczenie danych historii wydrukow i czasow pakowania", "Zerowanie aplikacji");

            return null;
        }

Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
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)