2010-11-16 15 views
26

Obecnie, kiedy potrzebuję udostępnić metodę taką jak processParams(params) między różnymi kontrolerami, używam dziedziczenia lub usług. Zarówno rozwiązanie ma pewne inconvenients:W jaki sposób udostępniasz popularne metody w różnych kontrolerach Grails?

  • dziedziczenie, nie można użyć wielokrotne dziedziczenie, co oznacza, że ​​trzeba mieć wszystkich metod użytkowych kontrolerów w jednym miejscu. A także, istnieje błąd w grails, który nie wykrywa żadnych zmian kodu w klasach kontrolera Base w trybie deweloperskim (musisz ponownie uruchomić aplikację)
  • Dzięki usługom nie masz dostępu do wszystkich wstrzykniętych właściwości, takich jak paramery, sesja, flush ...

Moje pytanie brzmi: czy istnieje jakikolwiek inny sposób wykorzystania niektórych popularnych metod dostępnych dla wielu kontrolerów?

Odpowiedz

3

Nie pomoże to w przypadku ponownego uruchomienia w trybie programowania, ale w ten sposób rozwiązałem ten problem. Jest brzydka i prawdopodobnie nie jest dobrą praktyką, ale przypisuję wspólny kod do klas jako zamknięć. Następnie można zrobić coś takiego:

new ControllerClosures().action(this) 

oraz z ze w klasie controllerClosures

def action={ 
    it.response.something 
    return [allYourData] 
} 
2

Można użyć Delegation design pattern:

class Swimmer { 
    def swim() { "swimming" } 
} 

class Runner { 
    def run() { "running" } 
} 

class Biker { 
    def bike() { "biking" } 
} 

class Triathlete { 
    @Delegate Swimmer swimmer 
    @Delegate Runner runner 
    @Delegate Biker biker 
} 

def triathlete = new Triathlete(
    swimmer: new Swimmer(), 
    runner: new Runner(), 
    biker: new Biker() 
) 

triathlete.swim() 
triathlete.run() 
triathlete.bike() 

W przypadku kontrolera przypisz pomocnika klasa bezpośrednio w polu instancji (lub konstruktorze nullary):

class HelperClass { 
    def renderFoo() { render 'foo' } 
} 

class FooController { 
    private @Delegate HelperClass helperClass = new HelperClass() 

    def index = { this.renderFoo() } 
} 

Informacje o typie delegate zostają wkompilowane w klasę zawierającą.

+6

Klasa delegat nie mają dostępu do wewnętrznych klasy właściciela, takich jak metoda lub params renderowania . – ataylor

+2

To niestety prawda, a tym samym '@ Delegate' nie będzie metodologią wyboru w tym przypadku. – robbbert

22

Jedną z opcji, którą lubię, jest pisanie wspólnych metod jako kategorii, a następnie w razie potrzeby łączenie jej z kontrolerami. Daje dużo większą elastyczność niż dziedziczenie, ma dostęp do takich rzeczy jak paramery, a kod jest prosty i zrozumiały.

Oto mały przykład:

@Category(Object) 
class MyControllerCategory { 
    def printParams() { 
     println params 
    } 
} 

@Mixin(MyControllerCategory) 
class SomethingController { 

    def create = { 
     printParams() 
     ... 
    } 

    def save = { 
     printParams() 
    } 
} 
+7

W rzeczywistości nie potrzebujesz adnotacji "@ Category" do używania adnotacji '@ Mixin'. - Potrzebujesz go tylko wtedy, gdy używasz alternatywnej metody 'SomethingController.mixin ([MyControllerCategory])' (która wymaga 'MyControllerCategory' do dostarczenia' statycznej 'metody' printParams() '). Dokumenty Groovy dostarczają próbek podobnych do twojego, ale nie jest to konieczne. - To nieporozumienie może wynikać z faktu, że korzystanie z * kategorii * podlega procesowi ewolucyjnemu - niegdyś niezgrabne ze względu na składnię, a obecnie zastąpione adnotacją "@ Mixin". – robbbert

+2

Na koniec główną różnicą między '@ Mixin' i' @ Delegate' jest to, że * delegate * metody są statycznie kompilowane do klasy docelowej, podczas gdy metody * mixin * są wywoływane w środowisku wykonawczym. – robbbert

+0

Czy istnieje sposób na uzależnienie wtrysku 'MyControllerCategory' od wstrzyknięcia zależności? –

3

Wspólna funkcjonalność jest wezwaniem do nowej klasy, niekoniecznie wspólny przodek. W sformułowaniu pytania brakuje oświadczenia o odpowiedzialności za to. Nie trzeba dodawać, że to jedna odpowiedzialność, za którą tworzymy nową klasę. Podejmuję kolejne decyzje w oparciu o odpowiedzialność klasową.

Wolę hybrydę robbbert 's oraz Jared' s odpowiedzi: I skonstruować dodatkowe zajęcia, przekazując im niezbędne wewnętrzne kontrolera jako parametry. Czasami zajęcia rozwijają się od method objects. odczuwalna:

def action = { 
    def doer = SomeResponsibilityDoer(this.request, this.response) 
    render doer.action() 
} 

Nie tak krótka, ale pozwala uzyskać kod pod testów i utrzymać niski sprzęgła.

Jako, że SomeResponsibilityDoer będzie mieć tylko kilka pól - poproś o odpowiedź - nie jest to wielka sprawa przy każdym wniosku.

Nie jest również wielka sprawa konieczności SomeResponsibilityDoer nie przeładować na zmiany kontrolera w dev, ponieważ:

  1. Początkowo można zadeklarować w niektórych plików Controller - zostanie przeładowana. Po jej zakończeniu, miejmy nadzieję, że nie będzie się często zmieniać, więc przenieś go do src/groovy.
  2. Jeszcze ważniejsze jest to, że projektowanie przebiega szybciej i lepiej w ramach testów jednostkowych niż podczas uruchamiania aplikacji i ponownego ładowania Contoller.
1

Można napisać całą wspólną metodę w commonService i używać tej usługi do envoke metoda commmon

Powiązane problemy