User or UserManager ? Problems of Observer Pattern

Ò

Ò»Ê×Ê«

#This is a real world problem I met.
#
#We have a database containing serveral tables, like : user, role,
organization. Each 2 of them has m:n relationships. These relations
are stored in association tables.
#
#Not we have to load all these data in memory to gain higher
performances. We create 3 classes , User, Role, Organization, to hold
these data.
#
#The question is : should I implement "add/delete/update" as methods
of User, or should I add another class UserManager?


#----------------
# Choice 1
#----------------

user_dict = {}
role_dict = {}

class User:
on_del_funcs = []
roles = []

def del(self):
user_dict.pop(self.name)

for f in self.on_del_funcs:
f(self)

# Using Observer Pattern to notify this user is deleted.
def addUserDelListener(self, on_del_func):
on_del_funcs.append(on_del_funcs)

def delUserDelListener(self, on_del_func):
on_del_funcs.remove(on_del_func)

class Role:

users = []

def addUsser(self, user):
self.users.append(user)
user.roles.append(self)
users.addUserDelListener(self.onUserDel)

def onUserDel(self, user):
self.users.remove(user)
user.delUserDelListener(self.onUserDel)

#----------------
# Choice 2
#----------------

class UserManager:

users = []

@classmethod
def delUser(cls, user):
cls.users.remove(user)

RoleManager.onUserDel(user)

class RoleManager:

roles = []

@classmethod
def onUserDel(cls, user):
for r in cls.roles.items():
r.users.remove(user)


# These codes are not complete, but that's enough to show my question.
# The first choice, which use Observer Pattern, has a very big
problem.
# When calling addUserDelListener, user got not only a callback
function, it
# also add reference count of role. So when we want to delete a
role. We have
# to carefully remove all *listener* hold by other objects, otherwise
this role
# will never be garbage collected.

# Not a big problem for only 2 classes. But when there are 10
*subject* classes which
# Role want to *observe*, 10 listeners has to be removed when delete a
role.
# And if these *subject* have 5 different types of events, then 50
listeners has
# to be removed.

# Actually I tried this approach once, and it is still a big headache
for 10
# programmers. ( The company I worked at that time hate any change to
so-called
# working code )



# The second choice, which I am using right now, actually has nothing
to do with
# Object Oriented Designing. The manager classes are not real
classes, just
# container of methods. These User/Role classes are only container
of data,
# not behavior.
#
# For many times, I asked my self, "Is it too hard for me to see the
power of
# OO, or I have just never met a problem suitable for a OO resolution?"
 
M

Michele Simionato

#----------------
# Choice 2
#----------------

class UserManager:

users = []

@classmethod
def delUser(cls, user):
cls.users.remove(user)

RoleManager.onUserDel(user)

class RoleManager:

roles = []

@classmethod
def onUserDel(cls, user):
for r in cls.roles.items():
r.users.remove(user)

Choice #2 is better, but it should not be implemented
this way. Using a class as a pure container of methods,
a big singleton, makes little sense, since you would
be better off with a module and old fashioned procedural
programming. You recognize this and are worried about this.
You do not need to be worried to much,
since there is nothing wrong with procedural design
(but you should use modules not classes). If you want
to be more object oriented, the first thing is to understand
is that you define objects in order to pass them to
other objects. For instance, you are using a global
list of users as a class attribute, but I would
just pass the list in the __init__ method, and
make it an instance attribute. I also would make
a RoleManager instance and pass it to the UserManager,
so that it can be used directly. Or perhaps I would
define a single UserRoleManager doing all the job.
It depends.
But all the point of OOP is to pass objects to other objects.
It is not bad to think of a data object as of a record
on steroids. Objects which are purely data and not
behavior are the simplest and the best objects, start
from them.
There are also objects which are mostly behavior
and nearly no data (the Manager objects here, which
just act over collections of other objects). Those
are more difficult to write. OTOH, nobody forces you
to write the managers as objects. You may find easier
to write a set of manager functions. This set of
functions can later become methods of a Manager object,
if it seems fit, but that must be done a posteriori,
not a priori.
Perhaps you should read some book like "Programming Python"
by Ludz which has many examples of how to write OO applications
in Python (I mean no offense, but indeed from you examples
is seems to me that you are missing the point of OOP,
as you are the first to recognize).
HTH,

Michele Simionato
 
Ò

Ò»Ê×Ê«

Thanks for your advice.

I studied python from the tutorial and the library manual, and I think
I am familiar enough with Python's grammar and API. That's why I
never thought I need to read a book of Python.

But if "Programming Python" also explains OO, I would be happy to read
it.
In fact, I am also planning to read again "Head First Object-Oriented
Design and Analysis".
I thought it's time for me to try again to find what is the *point* of
Obejct.

Anyway, this time I will be patient enough. I won't touch my code
until I am sure find some way to improve it.

#----------------
# Choice 2
#----------------
class UserManager:
users = []
@classmethod
def delUser(cls, user):
cls.users.remove(user)

class RoleManager:
roles = []
@classmethod
def onUserDel(cls, user):
for r in cls.roles.items():
r.users.remove(user)

Choice #2 is better, but it should not be implemented
this way. Using a class as a pure container of methods,
a big singleton, makes little sense, since you would
be better off with a module and old fashioned procedural
programming. You recognize this and are worried about this.
You do not need to be worried to much,
since there is nothing wrong with procedural design
(but you should use modules not classes). If you want
to be more object oriented, the first thing is to understand
is that you define objects in order to pass them to
other objects. For instance, you are using a global
list of users as a class attribute, but I would
just pass the list in the __init__ method, and
make it an instance attribute. I also would make
a RoleManager instance and pass it to the UserManager,
so that it can be used directly. Or perhaps I would
define a single UserRoleManager doing all the job.
It depends.
But all the point of OOP is to pass objects to other objects.
It is not bad to think of a data object as of a record
on steroids. Objects which are purely data and not
behavior are the simplest and the best objects, start
from them.
There are also objects which are mostly behavior
and nearly no data (the Manager objects here, which
just act over collections of other objects). Those
are more difficult to write. OTOH, nobody forces you
to write the managers as objects. You may find easier
to write a set of manager functions. This set of
functions can later become methods of a Manager object,
if it seems fit, but that must be done a posteriori,
not a priori.
Perhaps you should read some book like "Programming Python"
by Ludz which has many examples of how to write OO applications
in Python (I mean no offense, but indeed from you examples
is seems to me that you are missing the point of OOP,
as you are the first to recognize).
HTH,

Michele Simionato
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top