Archive

Archive for the ‘Design patterns’ Category

Strategy pattern in python

January 27, 2010 4 comments

Here is an example of strategy pattern in python. The alogrithm that varies in this example is the conversion of csv file to xml, html and any other formats. The client is the CsvUtil class. As you can see we can change the conversion at run time and also we can apply all the alogrithm if we wish to which we can’t achieve when we go for the regular inheritance model.  Please do note that an abstract class is equivalent to interface when there is no implementation.

Source code for all the classes related to strategy.

HTML_TEMPATE="""<html><body>%s</body></html>
"""
class CsvConverter:
	''' Abstract class or interface for conversion of csv file'''

	def convert(self,csv):
		pass #abstract method

class Csv2Html(CsvConverter):
	''' Covert Csv to html table with alternativing row colors'''

	def convert(self,csv):
		''' convert the csv file to html file '''
		htmltable = ''
		csvfile = open(csv)
		dialect = Sniffer().sniff(csvfile.read(2048))
		csvfile.seek(0) #rewind after sniffing
		csvreader = reader(csvfile,dialect)
		i = 0
		for line in csvreader:
			#print line
			if i == 0: #header row
					htmltable += '<tr class="head">'
			elif i % 2 == 0: #even row
					htmltable += '<tr class="even">'
			else: #odd row
					htmltable += '<tr class="odd">'
			for col in line:
				htmltable += '<td>%s</td>'%col
			htmltable += '</tr>'
			i += 1
		return HTML_TEMPLATE%htmltable

	def __str__(self):
		return 'Csv2Html'

class Csv2Xml(CsvConverter):
	''' Covert Csv to Xml '''

	def __init__(self,linetag='row'):
		''' Constructor '''
		self.linetag = linetag

	def convert(self,csv):
		''' convert the csv file to xml file '''
		print 'hello'
		xml = ''
		csvfile = open(csv)
		csvreader = DictReader(csvfile)
		for row in csvreader:
			xml += '<%s>'%self.linetag
			for colname,colvalue in row.iteritems():
				xml +=  '<%s>$%s</%s>'%(colname,colvalue,colname)
			xml += '</%s>'%self.linetag
		return xml

	def __str__(self):
		return 'Csv2Xml'

Here is the source code for the context. You can see the use of property in python here.

class CsvUtil(object):
	''' A wrapper or utility class for dealing with csv files '''

	def __init__(self,csv):
		''' constructor '''
		self.csv = csv
		self._numlines = None #this information is computed lazily
		self._numcols = None #this information is gathered lazily
		self._wellformed = None #this information is gathered lazily
		self._converter = Csv2Html() #default converter
	
	def _gatherstats(self):
		''' gather basic stats on the csv '''

		with open(self.csv) as csvfile:
			dialect = csv.Sniffer().sniff(csvfile.read(2048))
			csvfile.seek(0) #rewind
			reader = csv.reader(csvfile,dialect)
			self._numlines = 0
			self._wellformed = True
			self._numcols = 0
			for line in reader:
				self._numlines += 1
				if self._numcols != len(line):
					self._wellformed = False
	
	def convert(self):
		''' convert the csv file to another format '''

		return self._converter.convert(self.csv)
		
	def get_wellformed(self):
		''' a csv is wellformed if all the lines have same number of columns '''
		
		if not self._wellformed:
			self._gatherstats()
		return self._wellformed
	
	def get_numlines(self):
		''' number of lines in the csv files '''

		if not self._numlines:
			self._gatherstats()
		return self._numlines
	
	def get_numcols(self):
		''' number of lines in the csv files '''

		if not self._numcols:
			self._gatherstats()
		return self._numcols


	def get_converter(self):
		''' getter for converter '''
		return self._converter

	def set_converter(self,value):
		print 'set ',value
		self._converter = value

	#properties 	
	wellformed = property(get_wellformed)
	numlines = property(get_numlines)
	numcols = property(get_numcols)
	converter = property(get_converter,set_converter)

Finally the demonstration of using the context and strategy….

       csvfile = "C:\sandbox\python\mycsv.txt"
	util = 	CsvUtil(csvfile)  # my utility class for handling csv
	print util.numlines
	print util.wellformed
	print 'Default converter is',util.converter
	print util.convert() #print using the default converter 

	#now changing my converter at run time :-)
	util.converter = Csv2Xml()
	print 'My new converter is',util.converter
	print util.convert() 


Template method design pattern in python.

January 13, 2010 2 comments

Here is an example of implementing template method in python. This example assumes that ONLY meat in the burrito varies and so the child class implements the template method of selecting the meat while rest of the work is done by the base class…

import sys

class AbstractBurrito:
	'Abstract class for the burrito'

	"""
	Constructor
	"""
	def __init__(self):
		self.meat = None
		
	def __heatTortilla(self):
		print 'Warming tortilla'

	def __addBeans(self):
		print 'Adding beans...'
	
	def __addSalsa(self):
		print 'Adding salsa' 
	
	def __addCorns(self):
		print 'Adding corns...'

	"""
	Our assumption is that the burritos vary ONLY by the meat in it.
	So this method calls the template method on the child class.
	Note: The template method on the child class should be non-private.
	"""
	def __addMeat(self):
		self.selectMeat()
		print 'Adding %s'%self.meat

	"""
	Steps to prepare a burrito
	"""
	def make(self):
		self.__heatTortilla()
		self.__addBeans()
		self.__addMeat()
		self.__addSalsa()
		self.__addCorns()
		print 'Your %s burrito is warm and ready'%self.meat
		print 'Thank You \n'

class ChickenBurrito(AbstractBurrito):

	"""
	Chicken burrito 
	"""
	def selectMeat(self):
		self.meat = 'Chicken'

class BeefBurrito(AbstractBurrito):

	"""
	Beef burrito
	"""
	def selectMeat(self):
		self.meat = 'Beef'

if __name__ == "__main__":
	myburrito = ChickenBurrito()
	myburrito.make()
	urburrito = BeefBurrito()
	urburrito.make()

Follow

Get every new post delivered to your Inbox.