Let's say we need to design a module to retrieve 4 statistics counters,
- Bytes Transmitted
- Bytes Received
- Packet Transmitted
- Packets Received
1: class StatsClass(object):
2:
3: def __init__(self):
4: self._counter = 0
5: self._name ="Abtract Statistics Class"
6:
7: def Get_Name(self):
8: return self._name
9:
10: def Get(self):
11: return self._counter;
12:
13: def Reset(self):
14: self._counter = 0;
15:
16: def Set(self,counter):
17: self._counter = counter;
18:
19: def PollFromHw(self):
20: print(self._name, "is poll statistics counter from hardware register")
21:
22:
23: class StatsBytesTransmitted(StatsClass):
24: def __init__(self):
25: self._name = "Bytes Transmitted"
26: self._counter = 0
27:
28: class StatsBytesReceived(StatsClass):
29: def __init__(self):
30: self._name = "Bytes Received"
31: self._counter = 0
32:
33: class StatsPacketsTransmitted(StatsClass):
34: def __init__(self):
35: self._name = "Packets Transmitted"
36: self._counter = 0
37:
38: class StatsPacketsReceived(StatsClass):
39: def __init__(self):
40: self._name = "Packets Received"
41: self._counter = 0
42:
Then, create a Factory Class
1: # Factory Class
2: class AbstractStatsFactory():
3: def CreateStats(self,strStats):
4: pass
5:
6: class StatsFactory(AbstractStatsFactory):
7: def CreateStats(self,strStats):
8: if( strStats == "Bytes Received"):
9: StatsObj = StatsBytesReceived()
10: elif ( strStats == "Bytes Transmitted"):
11: StatsObj = StatsBytesTransmitted()
12: elif ( strStats == "Packets Transmitted"):
13: StatsObj = StatsPacketsTransmitted()
14: elif ( strStats == "Packets Received"):
15: StatsObj = StatsPacketsReceived()
16: else:
17: StatsObj = None
18:
19: return StatsObj
Now, let's put them into use
1: def Use_StatsObj(StatsObj):
2: StatsObj.PollFromHw()
3: StatsObj.Set(100)
4: print(StatsObj.Get_Name(), "counter is ", StatsObj.Get())
5: StatsObj.Reset()
6: print(StatsObj.Get_Name(), "counter Reset. Now counter is ", StatsObj.Get())
7:
8:
9:
10: strStats = "Bytes Received"
11: StatsObj = StatsFactory().CreateStats(strStats)
12: Use_StatsObj(StatsObj)
13:
14: strStats = "Bytes Transmitted"
15: StatsObj = StatsFactory().CreateStats(strStats)
16: Use_StatsObj(StatsObj)
The output is
Bytes Received is poll statistics counter from hardware register
Bytes Received counter is 100
Bytes Received counter Reset. Now counter is 0
Bytes Transmitted is poll statistics counter from hardware register
Bytes Transmitted counter is 100
Bytes Transmitted counter Reset. Now counter is 0
No bad so far, though, I am not satisfied.
There is still an if-elif-else statement in the StatsFactory Class. It will be nice if the Factory class knows how many Statistics Objects are there, without explicitly instantiated these concrete objects .
In OO theme, you can use Reflection to do so. In Python, this is how I implement it
1: class StatsFactory2(AbstractStatsFactory):
2: def CreateStats(self,strStats):
3: # Get all subclass
4: for childClass in StatsClass.__subclasses__() :
5: obj = childClass()
6: if( strStats == obj.Get_Name()):
7: return obj
8: def ListStatsObjNames(self):
9: objList = list()
10: for childClass in StatsClass.__subclasses__() :
11: # print(childClass)
12: obj = childClass()
13: objList.append(obj.Get_Name())
14: return objList
15:
16:
Now, it's time to put the code into test
1: print("--- now use New Factory --- ")
2: strStats = "Bytes Received"
3: StatsObj = StatsFactory2().CreateStats(strStats)
4: Use_StatsObj(StatsObj)
5:
6: print("Counter can be read: ", StatsFactory2().ListStatsObjNames())
7: while(True):
8: strStats = input("Which counter do you want to know ?");
9: StatsObj = StatsFactory2().CreateStats(strStats)
10: if( StatsObj == None ):
11: break
12: Use_StatsObj(StatsObj)
13:
This is the Output
--- now use New Factory ---
Bytes Received is poll statistics counter from hardware register
Bytes Received counter is 100
Bytes Received counter Reset. Now counter is 0
Counter can be read: ['Bytes Transmitted', 'Bytes Received', 'Packets Transmitted', 'Packets Received']
Which counter do you want to know ?Bytes Transmitted
Bytes Transmitted is poll statistics counter from hardware register
Bytes Transmitted counter is 100
Bytes Transmitted counter Reset. Now counter is 0
Now, this is beauty part -- let's add two new statistics counters right after the StatsPacketsReceived
1: class StatsFcsError(StatsClass):
2: def __init__(self):
3: self._name = "Fcs Error"
4: self._counter = 0
5:
6: class StatsDiscardPacket(StatsClass):
7: def __init__(self):
8: self._name = "Discard Packets"
9: self._counter = 0
10:
Then, run my test code without any change. Voila, the two new classes are automatically recognized and can be used.
--- now use New Factory ---
Bytes Received is poll statistics counter from hardware register
Bytes Received counter is 100
Bytes Received counter Reset. Now counter is 0
Counter can be read: ['Bytes Transmitted', 'Bytes Received', 'Packets Transmitted', 'Packets Received', 'Fcs Error', 'Discard Packets']
Which counter do you want to know ?Discard Packets
Discard Packets is poll statistics counter from hardware register
Discard Packets counter is 100
Discard Packets counter Reset. Now counter is 0
Which counter do you want to know ?
Beautiful, isn't it ?