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).