By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,728 Members | 2,480 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,728 IT Pros & Developers. It's quick & easy.

OO refactoring trial ??

P: n/a
OO refactoring trial
====================
Following is a simple trial structure of a refactoring (top-down to OO)
learning exercise I'm doing. Whether you call it a Factory pattern, COR
pattern, or some hinze 57, I don't know what class to use till run time and
I'm trying to avoid a lengthy "if" sequence, the test sequence is important,
and to avoid code duplication I'll be using code objects in the "doit"
methods.

You've already given me many good ideas in previous threads and this is where
it got you :~) This works, but would you please tell me:
1) What you don't like about the approach
2) The implications of using this in a recursive approach (referenced from
but outside the recursive function) and if the decorators help with such.
3) Any other comments you might offer

Thank you,
Lee C
=========== ootest.py
============
class MF(object):
@staticmethod
def findit(t):
for it in MF.__subclasses__():
if it.testit(t):
return it().doit

class A(MF):
@staticmethod
def testit(tv):
if tv == 'relates to A':
return True
else:
return False

def doit(self):
print '# did A #'

class B(MF):
@staticmethod
def testit(tv):
if tv == 'relates to B':
return True
else:
return False

def doit(self):
print '# did B #'

mydoit = MF.findit('relates to B')
mydoit()

mydoit = MF.findit('relates to A')
mydoit()

======== Test run ==============
Python 2.4.1 (#2, Mar 31 2005, 00:05:10)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)]
Type "help", "copyright", "credits" or "license" for more information.
import ootest # did B #
# did A #


Jul 19 '05 #1
Share this Question
Share on Google+
10 Replies


P: n/a
Chinook wrote:
3) Any other comments you might offer if*tv*==*'relates*to*A':
return*True
else:
return*False


Make that

return tv == 'relates to A'

lest your zen master hit you.

Peter
Jul 19 '05 #2

P: n/a
On Tue, 28 Jun 2005 02:22:13 -0400, Peter Otten wrote
(in article <d9*************@news.t-online.com>):
Chinook wrote:
3) Any other comments you might offer

if*tv*==*'relates*to*A':
return*True
else:
return*False


Make that

return tv == 'relates to A'

lest your zen master hit you.

Peter


Thank you Peter,

So wrapped up in the OO I overlooked the simpler aspects.

Lee C
Jul 19 '05 #3

P: n/a
Lee,

Interesting idea, but I think the technique of "inherit from MF to
automatically add class to the test chain" is a gimmick that wont
scale.

Here are some things to consider:

- I'm not keen on the coupling of forcing your A,B,etc. classes to
inherit from MF. Especially in a duck-typing language like Python, it
adds no value, the subclasses receive no default behavior from their
superclass, and I'm not keen on using the inheritance hierarchy to
register test classes. What is the order of classes returned from
__subclasses__()? Will you always want this order? Will you always
want all subclasses? If this is part of a library that others will
use, you may not be able to anticipate what subclasses someone else may
stick on to your MF class.

- The list of items should be dynamic in the calling code, not built
statically by your class structure. There's no way to anticipate what
various sequences you will want to evaluate.

- Let's call the MF class "MultiEvaluator". There are several ways to
evaluate among several alternatives:
. short-circuit, take first match
. best-fit, evaluate all and take best score (assuming the testit
routines return a double or int, as opposed to a bool)

For short-circuiting, say you have a case that will match A, you want
to avoid any processing related to B,C,etc. as possible at test/do
runtime. You *might* be able to do extra work at list construction
time. Consider these two alternative designs:

class MultiEvaluator(object):
def __init__(self, *classes):
self.testClasses = classes[:]

def findit(self, *args):
for C in self.testClasses:
testobj = C()
if testobj.testit(args[0]):
testobj.doit()

MultiEvaluator(A,B).findit("relates to A")

vs.

class MultiEvaluator(object):
def __init__(self, *classes):
self.testClasses = [ C() for C in classes ]

def findit(self, *args):
for testobj in self.testClasses:
if testobj.testit(args[0]):
testobj.doit()

MultiEvaluator(A,B).findit("relates to B")

In the first case, no B class is ever constructed, so if test object
construction is expensive, you might go this route. In the second, A()
and B() are built ahead of time, so the run-time processing is the
fastest - you might choose this if the construction time can be done up
front. The second option may cause problems for multi-threadedness or
re-entrancy with a shared MultiEvaluator, though.

This style of constructing the MultiEvaluator also makes more explicit
(which I hear is better than implicit) what classes you are testing
when creating your MultiEvaluator.

- To make your testit() and doit() code more flexible and more
powerful, I'd pass (*args) to each, as in:

class MultiEvaluator(object):
def __init__(self, *classes):
self.testClasses = classes[:]

def findit(self, *args):
for C in self.testClasses:
testobj = C()
if testobj.testit(args):
testobj.doit(args)

- In the interests of flogging OO-ness, you could make MultiEvaluator
into a function object, by changing "findit" to "__call__". Then your
invocation of it would change from:

getObj = MultiEvaluator(A,B)
getObj.findit("relates to B")

to:

getObj = MultiEvaluator(A,B)
getObj("relates to B")

Although some might claim this is *less* explicit. The purpose of this
is that you could then pass getObj to functional routines like map
(although you could also pass getObj.findit).

Those are my comments off the top of my head. Let us know what you
come up with.

-- Paul

Jul 19 '05 #4

P: n/a
On Tue, 28 Jun 2005 10:22:59 -0400, Paul McGuire wrote
(in article <11*********************@g43g2000cwa.googlegroups. com>):
Lee,

Interesting idea, but I think the technique of "inherit from MF to
automatically add class to the test chain" is a gimmick that wont
scale.

Here are some things to consider:

- I'm not keen on the coupling of forcing your A,B,etc. classes to
inherit from MF. Especially in a duck-typing language like Python, it
adds no value, the subclasses receive no default behavior from their
superclass, and I'm not keen on using the inheritance hierarchy to
register test classes. What is the order of classes returned from
__subclasses__()? Will you always want this order? Will you always
want all subclasses? If this is part of a library that others will
use, you may not be able to anticipate what subclasses someone else may
stick on to your MF class.
Even though this is an exercise, it is a learning exercise and your points
are taken as pertinent considerations in "real-world" adaptation.

So basically I'm coming from a if:elif:...else: test sequence where the order
of the tests is critical (could be multiple hits and the first should be
taken). My first trial was just to move the "if" sequence to a factory
method. My second trial was to initially create an ordered list of classes
to be tested as in:
class A(object): .... def foo(self):
.... print "I'm A.foo"
.... class B(object): .... def foo(self):
.... print "I'm B.foo"
.... co = [A(), B()]
co[1].foo() I'm B.foo


My third trial is what you are responding to. So in the "real-world" I could
only suggest how someone else might add a test, but just as in the top-down
"if" sequence I could impose no discipline. Even adding a weighting/priority
to test cases (and going through all) is a halfway measure (though possibly a
more explicit one). I think that the consideration of how a test case might
be added in the future is very important - having dug my way through many
other programs.

BTW: Is duck-typing a variation on duct-taping?

- The list of items should be dynamic in the calling code, not built
statically by your class structure. There's no way to anticipate what
various sequences you will want to evaluate.
I was also playing with another exercise involving dynamic test cases where I
thought the ordered list (second trial) would be a potential approach?

- Let's call the MF class "MultiEvaluator". There are several ways to
evaluate among several alternatives:
. short-circuit, take first match
what I was shooting for in this exercise
. best-fit, evaluate all and take best score (assuming the testit
routines return a double or int, as opposed to a bool)
like my weighted/priority thought?

For short-circuiting, say you have a case that will match A, you want
to avoid any processing related to B,C,etc. as possible at test/do
runtime. You *might* be able to do extra work at list construction
time. Consider these two alternative designs:

class MultiEvaluator(object):
def __init__(self, *classes):
self.testClasses = classes[:]

def findit(self, *args):
for C in self.testClasses:
testobj = C()
if testobj.testit(args[0]):
testobj.doit()

MultiEvaluator(A,B).findit("relates to A")
my only drawback on such would be ME(A,B,...Z).findit(test_data)
Explicit but cumbersome maybe, though no where near as cumbersome as a
lengthy "if" sequence.

vs.

class MultiEvaluator(object):
def __init__(self, *classes):
self.testClasses = [ C() for C in classes ]

def findit(self, *args):
for testobj in self.testClasses:
if testobj.testit(args[0]):
testobj.doit()

MultiEvaluator(A,B).findit("relates to B")

In the first case, no B class is ever constructed, so if test object
construction is expensive, you might go this route. In the second, A()
and B() are built ahead of time, so the run-time processing is the
fastest - you might choose this if the construction time can be done up
front. The second option may cause problems for multi-threadedness or
re-entrancy with a shared MultiEvaluator, though.

This style of constructing the MultiEvaluator also makes more explicit
(which I hear is better than implicit) what classes you are testing
when creating your MultiEvaluator.

- To make your testit() and doit() code more flexible and more
powerful, I'd pass (*args) to each, as in:
The testit() methods do in the full-fledged exercise. The doit() methods
return code objects (but I'm rethinking that).

class MultiEvaluator(object):
def __init__(self, *classes):
self.testClasses = classes[:]

def findit(self, *args):
for C in self.testClasses:
testobj = C()
if testobj.testit(args):
testobj.doit(args)
As in my ordered list approach (and what I thought my "cleverly" generated
list would also do before giving it more thought), the idea in this exercise
is to have one list (created once) that all levels of recursion would use.

Your forcing me to use my head :~)

- In the interests of flogging OO-ness, you could make MultiEvaluator
into a function object, by changing "findit" to "__call__". Then your
invocation of it would change from:

getObj = MultiEvaluator(A,B)
getObj.findit("relates to B")

to:

getObj = MultiEvaluator(A,B)
getObj("relates to B")

Although some might claim this is *less* explicit. The purpose of this
is that you could then pass getObj to functional routines like map
(although you could also pass getObj.findit).

Good point, but my objective is an OO learning exercise. My mind isn't as
flexible as it was back in the 60s :~) so it takes more effort to get a
handle on something new (to me).


Those are my comments off the top of my head. Let us know what you
come up with.

-- Paul


Thank you very much for taking the time to consider and respond,

Lee C

Jul 19 '05 #5

P: n/a
Never mind.

BTW: Is duck-typing a variation on duct-taping?


http://www.rubygarden.org/ruby?DuckTyping

http://en.wikipedia.org/wiki/Duck_typing
Lee C

Jul 19 '05 #6

P: n/a
Paul,

Going back over various material led to another question regarding your
comments.
- I'm not keen on the coupling of forcing your A,B,etc. classes to
inherit from MF. Especially in a duck-typing language like Python, it
adds no value, the subclasses receive no default behavior from their
superclass, and I'm not keen on using the inheritance hierarchy to
register test classes.


The latter (__subclasses__() bit) I picked up on as a convenience (courtesy
of a suggestion by STeVe) and I understand your point about more explicit
ordering. However, the former seems to take issue with examples like:

http://jamesthornton.com/eckel/TIPython/html/Sect08.htm

or indirectly as a show stopper (using inheritance) as in:

http://www.unc.edu/~mconway/mt_stati...odds_and_ends/

which is more in line with your comments.

Anything more to this than individual preferences? Might it not be more
explicit at least? Anyone?

Thanks,

Lee C

Jul 19 '05 #7

P: n/a
'''
You might find this interesting. Note that the object creation in
main() below could easily be read in from a text file instead,
thus meeting your requirement of not knowing an item's class
until runtime.

Sample output:

{'password': 'Your Password Here', 'type': 'A', 'logonid': 'Your
Logonid Here'}
# did A #
{'ssn': 555555555, 'type': 'B'}
# did B #
{'type': 'BASE', 'address': '501 south street'}
# did BASE #
None
'''

def Create(type = 'BASE', **kwargs):
if type not in _CLASSES:
return None # Or return a default object
obj = _CLASSES[type](type = type, **kwargs)
return obj

class BASE(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __str__(self):
return str(self.__dict__)
def doit(self):
print "# did BASE #"

class A(BASE):
def doit(self):
print '# did A #'

class B(BASE):
def doit(self):
print '# did B #'

_CLASSES = {'BASE': BASE, 'A': A, 'B': B}

def main():
obj1 = Create(type = 'A', logonid = 'Your Logonid Here', password =
'Your Password Here')
print obj1
obj1.doit()
obj2 = Create(type = 'B', ssn = 555555555)
print obj2
obj2.doit()
obj3 = Create(address = '501 south street')
print obj3
obj3.doit()
obj4 = Create(type = 'Missing')
print obj4

main()

Jul 19 '05 #8

P: n/a
Lee -

Bruce Eckel's observation:

"the above scaffolding of Obstacle, Player and GameElementFactory
(which was translated from the Java version of this example) is
unnecessary - it's only required for languages that have static
type checking. As long as the concrete Python classes follow the form
of the required classes, we don't need any base classes..."

is consistent with my "duck-typing" comment that all that is needed of
A,B,etc. is that they provide the necessary testit and doit methods in
the proper form. He also goes on to say that, even if the base classes
were empty, they still provide a useful handle for all of the derived
classes, to indicate they are all common subtypes of "Shape" or
whatever.

I think you were referring to the "Poking Python with a Stick" entry in
the second link. The inheritance of all taunts from Taunt allowed the
author to put some overriding common-to-all-taunts behavior into the
superclass, even if it was just a "sorry, not implemented" exception
into the base Taunt class's tauntTheKnights() method. The idea was to
provide a runtime exception if the needed method had not been
overridden in the subclass. But it also provides a place to put any
other common-to-all-taunts behavior, perhaps a common constructor, or
other common helper methods. I think this is a good use of
inheritance, when the base class actually adds some value to the
architecture, even if it is just Bruce Eckel's empty placeholder base
class. Note also that this author was doing patterns experimentation
in both Python and Java. I envision him writing the factory's
getInstance() method in Java, and having to declare its return value
type. In this case Java really drives you to having all possible
return values derive from a common base class, so that you can define
the method as:

Taunt getInstance(string tauntTypeTag) { ...

which is a bit easier to deal with than just declaring that getInstance
returns an object, and then having to cast it - in fact, if there were
no common base type, it would be problematic to know just what to cast
it to.

But Python does not require this sort of type rigidity. In your code,
the only requirement is that each class A,B,etc. have testit and doit
methods - they have to walk and talk like ducks, they don't need to
*be* ducks. My point in an earlier post was, I think many current
design patterns were born out of the C++ and Java worlds, and may be
overly restrictive for a comparable implementation in Python.

One step you might take in the MultiEvaluator constructor is to
validate the provided classes on input, so that you don't have to
exhaustively test all A thru Z classes before finding out that you
misspelled Z's doit method as "dooit". This goes one better than the
abstract subclass's "you forgot to override me" method implementation.

In sum, both of the links you sent used examples that had inheritance
hierarchies where the base class provided some contribution and
commonality among the derived classes. TauntArthur "is-a" Taunt,
TauntGalahad "is-a" Taunt. It really does look like the Factory
pattern should return objects that share a common base class. But in
Python, that common base class can also be just "something that walks
like a duck."

-- Paul

Jul 19 '05 #9

P: n/a
On Tue, 28 Jun 2005 23:23:43 -0400, Kamilche wrote
(in article <11*********************@g14g2000cwa.googlegroups. com>):
'''
You might find this interesting. Note that the object creation in
main() below could easily be read in from a text file instead,
thus meeting your requirement of not knowing an item's class
until runtime.

Sample output:

{'password': 'Your Password Here', 'type': 'A', 'logonid': 'Your
Logonid Here'}
# did A #
{'ssn': 555555555, 'type': 'B'}
# did B #
{'type': 'BASE', 'address': '501 south street'}
# did BASE #
None
'''

def Create(type = 'BASE', **kwargs):
if type not in _CLASSES:
return None # Or return a default object
obj = _CLASSES[type](type = type, **kwargs)
return obj

class BASE(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __str__(self):
return str(self.__dict__)
def doit(self):
print "# did BASE #"

class A(BASE):
def doit(self):
print '# did A #'

class B(BASE):
def doit(self):
print '# did B #'

_CLASSES = {'BASE': BASE, 'A': A, 'B': B}

def main():
obj1 = Create(type = 'A', logonid = 'Your Logonid Here', password = 'Your Password Here')
print obj1
obj1.doit()
obj2 = Create(type = 'B', ssn = 555555555)
print obj2
obj2.doit()
obj3 = Create(address = '501 south street')
print obj3
obj3.doit()
obj4 = Create(type = 'Missing')
print obj4

main()


Kamilche,

Yes it is interesting and explicit, thank you.

What I was not clear enough about though, is that at runtime the class object
creation depends on the information being processed. That is, the context
and type of node in the recursed tree. That's why in my trial the potential
classes all included a "testit" method and the order of testing is important.
Beyond that the "doit" methods all have some varying duplicated expressions,
so I was constructing their functionality with code objects. However, in
going back over researched material after Paul's comments I noticed another
technique that might be better in creating functionality and have to study it
further.

Thanks for the sample,

Lee C
Jul 19 '05 #10

P: n/a
On Wed, 29 Jun 2005 00:18:24 -0400, Paul McGuire wrote
(in article <11*********************@f14g2000cwb.googlegroups. com>):
Lee -

Bruce Eckel's observation:

"the above scaffolding of Obstacle, Player and GameElementFactory
(which was translated from the Java version of this example) is
unnecessary - it's only required for languages that have static
type checking. As long as the concrete Python classes follow the form
of the required classes, we don't need any base classes..."

is consistent with my "duck-typing" comment that all that is needed of
A,B,etc. is that they provide the necessary testit and doit methods in
the proper form. He also goes on to say that, even if the base classes
were empty, they still provide a useful handle for all of the derived
classes, to indicate they are all common subtypes of "Shape" or
whatever.

I think you were referring to the "Poking Python with a Stick" entry in
the second link. The inheritance of all taunts from Taunt allowed the
author to put some overriding common-to-all-taunts behavior into the
superclass, even if it was just a "sorry, not implemented" exception
into the base Taunt class's tauntTheKnights() method. The idea was to
provide a runtime exception if the needed method had not been
overridden in the subclass. But it also provides a place to put any
other common-to-all-taunts behavior, perhaps a common constructor, or
other common helper methods. I think this is a good use of
inheritance, when the base class actually adds some value to the
architecture, even if it is just Bruce Eckel's empty placeholder base
class. Note also that this author was doing patterns experimentation
in both Python and Java. I envision him writing the factory's
getInstance() method in Java, and having to declare its return value
type. In this case Java really drives you to having all possible
return values derive from a common base class, so that you can define
the method as:

Taunt getInstance(string tauntTypeTag) { ...

which is a bit easier to deal with than just declaring that getInstance
returns an object, and then having to cast it - in fact, if there were
no common base type, it would be problematic to know just what to cast
it to.

But Python does not require this sort of type rigidity. In your code,
the only requirement is that each class A,B,etc. have testit and doit
methods - they have to walk and talk like ducks, they don't need to
*be* ducks. My point in an earlier post was, I think many current
design patterns were born out of the C++ and Java worlds, and may be
overly restrictive for a comparable implementation in Python.

One step you might take in the MultiEvaluator constructor is to
validate the provided classes on input, so that you don't have to
exhaustively test all A thru Z classes before finding out that you
misspelled Z's doit method as "dooit". This goes one better than the
abstract subclass's "you forgot to override me" method implementation.

In sum, both of the links you sent used examples that had inheritance
hierarchies where the base class provided some contribution and
commonality among the derived classes. TauntArthur "is-a" Taunt,
TauntGalahad "is-a" Taunt. It really does look like the Factory
pattern should return objects that share a common base class. But in
Python, that common base class can also be just "something that walks
like a duck."

-- Paul


Thank you for the clarification Paul. I missed the influence of C++ and Java
origins because my past experience was more in the assembler realm with a
little C and Pascal, and now I'm trying to get up to speed with Python, ObjC
and the OO world. You've helped me look at what I'm reading in a better
light.

Thanks again,

Lee C
Jul 19 '05 #11

This discussion thread is closed

Replies have been disabled for this discussion.