1 """null.py
2
3 This is a sample implementation of the 'Null Object' design pattern.
4
5 Roughly, the goal with Null objects is to provide an 'intelligent'
6 replacement for the often used primitive data type None in Python or
7 Null (or Null pointers) in other languages. These are used for many
8 purposes including the important case where one member of some group
9 of otherwise similar elements is special for whatever reason. Most
10 often this results in conditional statements to distinguish between
11 ordinary elements and the primitive Null value.
12
13 Among the advantages of using Null objects are the following:
14
15 - Superfluous conditional statements can be avoided
16 by providing a first class object alternative for
17 the primitive value None.
18
19 - Code readability is improved.
20
21 - Null objects can act as a placeholder for objects
22 with behaviour that is not yet implemented.
23
24 - Null objects can be replaced for any other class.
25
26 - Null objects are very predictable at what they do.
27
28 To cope with the disadvantage of creating large numbers of passive
29 objects that do nothing but occupy memory space Null objects are
30 often combined with the Singleton pattern.
31
32 For more information use any internet search engine and look for
33 combinations of these words: Null, object, design and pattern.
34
35 Dinu C. Gherman,
36 August 2001
37
38 Karsten Hilbert
39 July 2004
40 """
41
42 __author__ = "Dinu C. Gherman, Karsten Hilbert"
43 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
44
45 import logging
46
47 _log = logging.getLogger('cNull')
48
49
51 """A class for implementing Null objects.
52
53 This class ignores all parameters passed when constructing or
54 calling instances and traps all attribute and method requests.
55 Instances of it always (and reliably) do 'nothing'.
56
57 The code might benefit from implementing some further special
58 Python methods depending on the context in which its instances
59 are used. Especially when comparing and coercing Null objects
60 the respective methods' implementation will depend very much
61 on the environment and, hence, these special methods are not
62 provided here.
63 """
64
66 "Ignore parameters."
67 _log.debug('args: %s', args)
68 _log.debug('kwargs: %s', kwargs)
69
70
72 "Ignore method calls."
73 _log.debug('args: %s', args)
74 _log.debug('kwargs: %s', kwargs)
75 return self
76
77
79 "Ignore attribute requests."
80 _log.debug('%s.%s', self, attribute)
81 return self
82
84 "Ignore attribute setting."
85 _log.debug('%s.%s = %s', self, attribute, value)
86 return self
87
89 "Ignore deleting attributes."
90 _log.debug('%s.%s', self, attribute)
91 return self
92
93
95 "Ignore item requests."
96 _log.debug('%s[%s]', self, item)
97 return self
98
100 "Ignore item setting."
101 _log.debug('%s[%s] = %s', self, item, value)
102 return self
103
105 "Ignore deleting items."
106 _log.debug('%s[%s]', self, item)
107 return self
108
109
111 "Return a string representation."
112 return "<cNull instance @ %s>" % id(self)
113
115 "Convert to a string and return it."
116 return '<cNull instance>'
117
119 _log.debug('returns 0')
120 return 0
121
123 _log.debug('0')
124 return 0
125
126
128 "Perform some decent tests, or rather: demos."
129
130
131
132 n = cNull()
133 n = cNull('value')
134 n = cNull('value', param='value')
135
136 n()
137 n('value')
138 n('value', param='value')
139
140
141
142 n.attr1
143 n.attr1.attr2
144 n.method1()
145 n.method1().method2()
146 n.method('value')
147 n.method(param='value')
148 n.method('value', param='value')
149 n.attr1.method1()
150 n.method1().attr1
151
152 n.attr1 = 'value'
153 n.attr1.attr2 = 'value'
154
155 n['1']
156 n['2'] = '123'
157 del n['3']
158
159 del n.attr1
160 del n.attr1.attr2.attr3
161
162
163 tmp = '<cNull instance @ %s>' % id(n)
164 assert repr(n) == tmp
165 assert str(n) == '<cNull instance>'
166
167
168 if n == 1:
169 print("Null object == 1")
170 else:
171 print("Null object != 1")
172
173
174 if __name__ == '__main__':
175
176 import sys
177
178 if len(sys.argv) < 2:
179 sys.exit()
180
181 if sys.argv[1] != 'test':
182 sys.exit()
183
184 test()
185