Delegacja

Delegacja pozwala zmienić zachowanie jednej (wybranej) metody lub kilku metod klasy bez zmiany jej typu (a przynajmniej - bez zmiany intyerfejsu). Osiąga się to przez schowanie obiektu klasy bazowej w środku obiektu klasy potomnej i odpowiednim przekierowaniu wywołań.

 1class InnerClass:
 2
 3    def my_method(self):
 4        return "metoda wewnętrzna"
 5
 6class SimpleDelegation:
 7
 8    def __init__(self,inner):
 9        self.__inner = inner
10
11    def my_method(self):
12        return "nadpisana " + self.__inner.my_method()
13
14
15if __name__ == "__main__" :
16
17    ic = InnerClass()
18    sd = SimpleDelegation(ic)
19    for obct in [ic,sd] :
20        print(obct.my_method())
21

Zauważ, że delegacje bardzo przypominają Pythonowe dekoratory.

Note

Delegacja opiera się na zawieraniu obiektu - obiekt klasy InnerClass jest schowany wewnątrz SimpleDelegation. Ten sam efekt można osiągnąć, wykorzystując dziedziczenie

Drugi przykład jest już bardziej praktyczny. Poniżej zdefiniowano klasę bazową AtomSelector, która zaznacza bądź nie zaznacza podane atomy. Klasa ta jest tylko interfejsem, definiuje metodę która pojawi się w klasach potomnych. Następnie zdefiniowano dwa konkretne selektory (SelectCA który zaznacza węgle alfa oraz SelectHeavyBB do zaznaczania ciężkich atomów łańcucha głównego białka).

 1from sys import argv
 2from Atom import Atom
 3
 4
 5class AtomSelector :
 6    """ Base class for atom selectors
 7    """
 8    def __call__(self, atom):
 9        raise NotImplementedError
10
11
12class SelectCA(AtomSelector) :
13    """ Selects alpha carbons
14    """
15    def __call__(self, atom):
16        if atom.name == " CA " : return True
17        return False
18
19
20class SelectHeavyBB(AtomSelector) :
21    """ Selects backbone heavy atoms
22    """
23    def __call__(self, atom):
24        if atom.name == " N  " or atom.name == " CA " \
25            or atom.name == " C  " or atom.name == " O  " : 
26            return True
27        return False
28
29
30class InvertSelector(AtomSelector) :
31
32    def __init__(self,selector) :
33        self.__selector = selector
34
35    def __call__(self, atom):
36        return not self.__selector(atom)
37
38
39class LogicORSelector(AtomSelector) :
40
41    def __init__(self) :
42        self.__selectors = []
43
44    def add_selector(self, sel):
45        self.__selectors.append(sel)
46
47    def __call__(self, atom):
48        for s in self.__selectors :
49            if s(atom): return  True
50        return False
51
52
53if __name__ == "__main__" :
54
55    # Read atoms from a PDB file
56    atoms = []
57    i = 1
58    for line in open(argv[1]) : # e.g. "2gb1.pdb"
59        if line.startswith("ATOM  ") :
60            atoms.append( Atom(i, line[12:16], line[76:78]) )
61    
62    # Create some selectors
63    is_ca = SelectCA()
64    is_bb = SelectHeavyBB()
65    is_sc = InvertSelector(SelectHeavyBB())
66    print("name   is_CA  is_BB  is_SC")
67    print("--------------------------")
68    for ai in atoms :
69        print("%4s   %5s  %5s  %5s" %(ai.name,is_ca(ai), is_bb(ai), is_sc(ai)))
70
71

Meritum tego przykładu (wzorzec delegacji) pojawia się w klasie InvertSelector, do odwrócenia zaznaczenia dowolnego obiektu. Sama klasa InvertSelector nie wie, co ma zaznaczyć; jedynie wykorzystuje do tego celu podany selektor (przechowywany jako prywatne pole klasy self.__selector), traktując go jako czarną skrzynkę. Następnie stosuje operator logiczny negacji do otrzymanego wyniku. Zauważ, że efektu tego nie można by uzyskać przez dziedziczenie. Owszem, można np. odziedziczyć po klasie SelectCA aby odwrócić to zaznaczenie:

1class SelectNotCA(SelectCA) :
2
3    def __call__(self, atom):
4        return not self.__selector(atom)

Klasa InvertSelector jednak może coś więcej: ona odwraca dowolne zaznaczenie, podane jako argument dla jej konstruktora. Kolejnym selektorem z delegacją jest LogicORSelector, który zwraca prawdę, gdy choć jeden z selektorów jest spełniony Zauważ, że InvertSelector oraz LogicORSelector są typu AtomSelector (dziedziczą po nim).