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 ?
can you share more on python design pattern? thanks
ReplyDeleteI read your post about Factory Method Pattern in Python. All the coding which you shared here is simple and also it is easy to understand. Thanks for sharing this.
ReplyDeleteLearning Games
I was reading that post and i can't understand the middle section of the post where you say i am not satisfied. Would you like to please elaborate it.
ReplyDeleteI am waiting for the update.
123movies