Co to jest idiom "wykonać wokół"?

Co to jest ten idiom "Execute Around" (lub podobny), o którym słyszałem? Dlaczego mogę go używać i dlaczego nie chcę go używać?

Author: Roman C, 2008-12-04

8 answers

Zasadniczo jest to wzorzec, w którym piszesz metodę robienia rzeczy, które są zawsze wymagane, np. alokacja zasobów i sprzątanie, i sprawiasz, że dzwoniący przechodzi w "co chcemy zrobić z zasobem". Na przykład:

public interface InputStreamAction
{
    void useStream(InputStream stream) throws IOException;
}

// Somewhere else    

public void executeWithFile(String filename, InputStreamAction action)
    throws IOException
{
    InputStream stream = new FileInputStream(filename);
    try {
        action.useStream(stream);
    } finally {
        stream.close();
    }
}

// Calling it
executeWithFile("filename.txt", new InputStreamAction()
{
    public void useStream(InputStream stream) throws IOException
    {
        // Code to use the stream goes here
    }
});

// Calling it with Java 8 Lambda Expression:
executeWithFile("filename.txt", s -> System.out.println(s.read()));

// Or with Java 8 Method reference:
executeWithFile("filename.txt", ClassName::methodName);

Kod wywołujący nie musi martwić się o stronę otwartą/sprzątającą-zajmie się nią executeWithFile.

To było szczerze bolesne w Javie, ponieważ zamknięcia były tak wordy, począwszy od Javy 8 wyrażeń lambda można zaimplementować jak w wielu innych języki (np. wyrażenia lambda C # lub Groovy), a ten szczególny przypadek jest obsługiwany od Javy 7 ze strumieniami try-with-resources i AutoClosable.

Chociaż" przydzielanie i czyszczenie " jest typowym przykładem, istnieje wiele innych możliwych przykładów-obsługa transakcji, logowanie, wykonywanie kodu z większymi uprawnieniami itp. Jest w zasadzie trochę podobny do wzorca metody szablonowej , ale bez dziedziczenia.

 137
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-12 14:57:01

Idiom Execute Around jest używany, gdy musisz zrobić coś takiego:

//... chunk of init/preparation code ...
task A
//... chunk of cleanup/finishing code ...

//... chunk of identical init/preparation code ...
task B
//... chunk of identical cleanup/finishing code ...

//... chunk of identical init/preparation code ...
task C
//... chunk of identical cleanup/finishing code ...

//... and so on.

Aby uniknąć powtarzania całego tego nadmiarowego kodu, który jest zawsze wykonywany "wokół" twoich rzeczywistych zadań, utworzysz klasę, która zajmie się tym automatycznie:

//pseudo-code:
class DoTask()
{
    do(task T)
    {
        // .. chunk of prep code
        // execute task T
        // .. chunk of cleanup code
    }
};

DoTask.do(task A)
DoTask.do(task B)
DoTask.do(task C)

Ten idiom przenosi cały skomplikowany nadmiarowy kod w jedno miejsce i pozostawia główny program o wiele bardziej czytelny (i możliwy do utrzymania!)

Spójrz na Ten post dla C# Przykład, oraz Ten artykuł {[10] } dla przykładu C++.

 44
Author: e.James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 20:55:19

Widzę, że masz tu znacznik Java, więc użyję Javy jako przykładu, mimo że wzorzec nie jest specyficzny dla platformy.

Chodzi o to, że czasami masz kod, który zawsze zawiera ten sam kocioł przed uruchomieniem kodu i po uruchomieniu kodu. Dobrym przykładem jest JDBC. Zawsze chwytasz połączenie i tworzysz instrukcję (lub instrukcję prepared) przed uruchomieniem rzeczywistego zapytania i przetworzeniem zestawu wyników, a następnie zawsze robisz to samo czyszczenie kotła w end -- zamknięcie instrukcji i połączenia.

Pomysł z execute-around jest taki, że lepiej jest, jeśli możesz uwzględnić kod kotła. Oszczędza to trochę pisania, ale powód jest głębszy. Tu chodzi o zasadę "nie powtarzaj się" -wyizolowujesz kod w jednym miejscu, więc jeśli jest błąd, musisz go zmienić lub po prostu chcesz go zrozumieć, to wszystko jest w jednym miejscu.

Rzecz, która jest trochę trudne z tego rodzaju faktoring-out Jednak jest to, że masz odniesienia, które muszą zobaczyć zarówno części" przed", jak i" po". W przykładzie JDBC obejmowałoby to polecenie Connection oraz (Prepared). Aby więc poradzić sobie z tym, że zasadniczo "zawijasz" kod docelowy kodem kotła.

Możesz być zaznajomiony z niektórymi typowymi przypadkami w Javie. Jednym z nich są filtry servlet. Innym jest AOP wokół porad. Trzecia to wiosenne zajęcia xxxTemplate. W każdym przypadku masz jakiś obiekt wrapper, w którym twój "interesujący" kod (powiedzmy zapytanie JDBC i result set processing) jest wtryskiwany. Obiekt wrapper wykonuje część "przed", wywołuje interesujący kod, a następnie wykonuje część" po".

 7
Author: Willie Wheeler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-07 22:25:13

Zobacz także Code Sandwiches , który bada tę konstrukcję w wielu językach programowania i oferuje kilka ciekawych pomysłów badawczych. Jeśli chodzi o konkretne pytanie, dlaczego można go użyć, powyższy artykuł oferuje kilka konkretnych przykładów:

Takie sytuacje pojawiają się, gdy program manipuluje współdzielonymi zasobami. Interfejsy API dla zamków, gniazd, plików lub połączeń z bazami danych mogą wymagać program do jawnego zamknięcia lub wydania zasobu, który wcześniej nabyte. W języku bez usuwania śmieci, programista jest odpowiedzialny za przydzielanie pamięci przed jej użyciem i zwalnianie jej po jego użyciu. Ogólnie rzecz biorąc, różne zadania programistyczne wymagają program do dokonania zmiany, działania w kontekście tej zmiany oraz następnie cofnij zmianę. Takie sytuacje nazywamy kanapkami kodowymi.

I Później:

Kanapki z kodem pojawiają się w wielu sytuacjach programistycznych. Kilka wspólnych przykłady dotyczą nabycia i uwolnienie ograniczonych zasobów, takie jak blokady, deskryptory plików lub połączenia gniazd. In more przypadków ogólnych, każda tymczasowa zmiana stanu programu może wymagać code sandwich. Na przykład program oparty na GUI może tymczasowo ignorować wejścia użytkownika lub jądro systemu operacyjnego może tymczasowo wyłączyć sprzęt przerywa. Brak przywrócenia wcześniejszego stanu w tych przypadkach spowoduje poważne robaki.

Artykuł nie bada dlaczego Nie używać tego idiomu, ale opisuje dlaczego idiom łatwo się pomylić bez pomocy na poziomie językowym:

Wadliwy kod powstaje najczęściej w obecności wyjątki i związany z nimi niewidoczny przepływ sterowania. Rzeczywiście., specjalne funkcje językowe do zarządzania kodem powstają głównie w języki obsługujące wyjątki.

Jednak wyjątki nie są jedyną przyczyną wadliwego kodu kanapki. Za każdym razem, gdy wprowadzane są zmiany w kodzie body , nowe ścieżki sterowania mogą powstać to omija po kodzie. W najprostszym przypadku, a opiekun musi tylko dodać return oświadczenie do ciała kanapki do wprowadzić nową usterkę, która może prowadzić do cichych błędów. Gdy ciało kod jest duży i przed i po są szeroko rozdzielone, takie błędy może być trudne do wykrycia wizualnie.
 7
Author: Ben Liblit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-12 17:36:03

An Execute Around Method to miejsce, w którym przekazujesz dowolny kod do metody, która może wykonać kod setup i / lub teardown i wykonać kod pomiędzy.

Java nie jest językiem, w którym chciałbym to zrobić. Bardziej stylowe jest przekazywanie zamknięcia (lub wyrażenia lambda) jako argumentu. Chociaż obiekty są prawdopodobnie równoważne zamknięciom .

Wydaje mi się, że metoda Execute Around jest czymś w rodzaju Inwersja Sterowania (Dependency Injection) że można zmieniać ad hoc, za każdym razem wywołania metody.

Ale można go również interpretować jako przykład sprzężenia sterującego(mówiącego metodzie, co ma zrobić przez jej argument, dosłownie w tym przypadku).

 6
Author: Bill Karwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 21:14:18

To przypomina mi Schemat projektowania strategii . Zauważ, że link, na który wskazałem zawiera kod Java dla wzorca.

Oczywiście można wykonać "Execute Around", wykonując inicjalizację i czyszczenie kodu i po prostu przekazując strategię, która zawsze będzie owinięta w inicjalizację i czyszczenie kodu.

Jak w przypadku każdej techniki stosowanej w celu zmniejszenia powtarzalności kodu, nie powinieneś jej używać, dopóki nie będziesz miał co najmniej 2 przypadków, w których jest to potrzebne, a może nawet 3 (a la the Zasada YAGNI). Należy pamiętać, że powtarzanie usuwania kodu zmniejsza konserwację (mniej kopii kodu oznacza mniej czasu spędzonego na kopiowaniu poprawek w każdej kopii), ale także zwiększa konserwację (więcej całkowitego kodu). Tak więc koszt tej sztuczki polega na tym, że dodajesz więcej kodu.

Ten rodzaj techniki jest przydatny nie tylko do inicjalizacji i czyszczenia. Jest również dobry, gdy chcesz ułatwić wywołanie funkcji (np. możesz użyć go w kreatorze, aby " następny" a przyciski "poprzednie" nie potrzebują gigantycznych instrukcji case, aby zdecydować, co zrobić, aby przejść do następnej/poprzedniej strony.

 3
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 21:33:22

Postaram się wyjaśnić, tak jak czterolatkowi:

Przykład 1

Mikołaj przyjeżdża do miasta. Jego elfy kodują co chcą za jego plecami, i chyba że coś zmienią to się trochę powtarza:

  1. Pobierz papier do pakowania
  2. Pobierz Super Nintendo .
  3. owiń to.

LUB to:

  1. Pobierz papier do pakowania
  2. Pobierz Lalkę Barbie .
  3. owiń to.

.... ad nauseam milion razy z milionem różnych prezentów: zauważ, że jedyną różnicą jest Krok 2. Jeśli tylko krok drugi jest inny, to dlaczego Mikołaj duplikuje kod, tzn. dlaczego duplikuje kroki 1 i 3 milion razy? Milion prezentów oznacza, że niepotrzebnie powtarza kroki 1 i 3 milion razy.

Execute around pomaga rozwiązać ten problem. i pomaga wyeliminować kod. Kroki 1 i 3 są zasadniczo stałe, dzięki czemu Krok 2 jest jedyną częścią, która zmiany.

Przykład #2

Jeśli nadal go nie rozumiesz, oto inny przykład: pomyśl o piasku: chleb na zewnątrz jest zawsze taki sam, ale to, co jest w środku, zmienia się w zależności od rodzaju piasku, który wybierzesz (.np. szynka, ser, dżem, masło orzechowe itp.). Chleb jest zawsze na zewnątrz i nie musisz powtarzać tego miliard razy dla każdego rodzaju piasku, który tworzysz.

Teraz jeśli przeczytasz powyższe wyjaśnienia, być może znajdziesz je łatwiej zrozumieć. Mam nadzieję, że to Wyjaśnienie ci pomogło.

 3
Author: BKSpurgeon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-23 23:16:48

Jeśli chcesz groovy idiomy, tutaj jest:

//-- the target class
class Resource { 
    def open () { // sensitive operation }
    def close () { // sensitive operation }
    //-- target method
    def doWork() { println "working";} }

//-- the execute around code
def static use (closure) {
    def res = new Resource();
    try { 
        res.open();
        closure(res)
    } finally {
        res.close();
    }
}

//-- using the code
Resource.use { res -> res.doWork(); }
 0
Author: Florin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-17 01:27:22