Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

The division of (class) labor in OOP

by jeyroz (Monk)
on Jun 05, 2005 at 18:51 UTC ( [id://463747]=perlquestion: print w/replies, xml ) Need Help??

jeyroz has asked for the wisdom of the Perl Monks concerning the following question:

I am seeking confirmation of what seems to be a fairly straight-forward OOP practice (for others anyway).

Let's use a common example:

A certain application deals with a central "user" ... (this "user" is the subject of its own class). This user can register itself with the system, authenticate itself (create a session), logout (destroy its session), alter its profile information, conduct searches on other "users", etc.

Is it cleaner in practice to create a "User" class that has all of the abilities above OR a "User" and an "Authentication" class that divides the abilities above accordingly? The "Authentication" class would, of course, handle authentication of the user, session management, registration, etc. The "User" class would handle the altering of profile information, searches, etc. I am consistantly hung up on the division of labor (so to speak) between classes. I run into the same question when dealing with a "Display" class ... should the "Display" class display the forms needed to authenticate or leave that to the class that handles the processing logic? (My guess is the former rather than later).

There is a distinct possibility that I am not envisioning the structure and responsibilities correctly when it comes to OOP. I have been a procedural programer for a little too long.

As always, thank you all ahead of time for your advice and guidance.

author => jeyroz

Replies are listed 'Best First'.
Re: The division of (class) labor in OOP
by tilly (Archbishop) on Jun 06, 2005 at 04:56 UTC
    If you're getting confused about the design of your classes, here's a simple rule of thumb that often is appropriate.

    Make your classes be nouns, and your methods verbs.

    That is, classes are things in your design. Methods are what those things do. This isn't, of course, the only way to design an OO system. But it is a principle that generally leads to reasonable designs, and can sort out a lot of confusion about where the boundaries of class responsibility lie.

    With this principle in mind, User is a reasonable class. It represents a person. But Authentication is not a good class. It sounds too much like a verb. If you don't want to put authentication methods into User, the suggestion above to have a Session class makes a lot of sense. In that design responsibility for authentication gets shared. It is up to the Session to know who is logged in. But when you try to login, the Session will have to ask the User to verify the password.

    Let's apply this principle to the second problem. Display again sounds like an action, and therefore is a bad candidate for a class. For one thing you're going to have confusion about where methods go. (If I want to display information about a user, where does Display leave off and User begin?) How should you replace it? One approach is to say that Users know how to display themselves. But this turns out to be a bad factoring as you get many different ways in which user information is displayed - User is supposed to know about being a User, not about HTML, reporting, and so on.

    A popular solution is the Model-View-Controller paradigm. In this paradigm User is a Model class, it represents actual information about someone in your system. Your Views are your actual output code, generally these are not classes but are actual web pages. The Controller is where Model meets View. It specifies business rules about how and why the Model will change.

    In that paradigm, the answer to your question is as follows. You have code that displays and accepts the login attempt. That code calls the appropriate Controller to find out what View comes next. That Controller checks with the Model (in this case User and/or Session) to find out whether a login attempt succeeded, and then decides whether you're next going to have a login failure view (and if so then which one), or an account page.

    This may sound complex, but it isn't really. In practice you'll find that your views are very straightforward. Your Models are also straightforward. The icky logic winds up in the Controller. While it still gets icky as business rules pile up, you at least know where to look for that logic in your system.

    If you're interested, CGI::Application and CGI::Prototype are two modules to help you get going on writing MVC applications in Perl. Maypole and Catalyst bill themselves as frameworks to help you do the same, the former is simpler but will restrict you more if you want to vary from The Chosen Path. The latter is more complex and flexible. I know that you'll be able to get help at Perlmonks for the two modules that I named. I'm less sure that you'll get good help (though you may) for the frameworks.

      You should take a look at OO design patterns in general MVC is one of the main ones related to creating tiers/layers within your application. You'll find these problems/decisons are repeated in every project. If you are working in a team there can be big benefits in code standards by using a framework.

      Tilly's advice is good, the priciple is to constantly ask yourself if you have chosen the correct objects(things).

      Lookup 'software cohesion' also, the more you have discrete objects that can't be broken down into smaller entities the higher the cohesion of your design.

      So if you look at User class source file and see code that is not directly related to an operation on a user then refactor it

        My impression was that Catalyst is more complex. The documentation to Catalyst says, Catalyst is based upon Maypole, which you should consider for smaller projects. That reinforces my impression. And, I'm sorry, but your article did not dispell it. Yes, you got a lot done in 30 lines of code. But to do that work you need to know how a lot of moving pieces fit together, and there is (to me) some quite surprising use of directives embedded in comments that would take some getting used to.

        Besides, this Maypole intro took less code, with fewer visible moving parts (and less magic) to get an application that looks more useable.

        None of that means, of course, that Catalyst is particularly complex. Indeed, if it does its job, then working with it should be far from complex. But there is a definite learning curve associated with it.

Re: The division of (class) labor in OOP
by BrowserUk (Patriarch) on Jun 05, 2005 at 19:11 UTC

    Ask yourself will you ever authenticate anything other than a User?

    If yes, and if there will some commonality between the code to authenticate a User and that/those Other things then an Authenticate class maybe useful.

    Otherwise, just add authentication method(s) to the User and Other classes.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: The division of (class) labor in OOP
by Tanktalus (Canon) on Jun 05, 2005 at 20:57 UTC

    Let's see. You have a User. And you have some object that Authenticates said user (is the user who they say they are?) - this could be via /etc/passwd, LDAP, DB, or whatever (and by having a separate object, you can swap in and out different objects to do different types of authentication). In fact, to create a User object, the Authenticate object would be called, and it would create the User object (with a read-only flag that said it was authenticated). And then you have Authorisation (can this user do what they're asking to do?) - and different ways to store that authorisation This object would check the user's authentication flag to see if it is actually authenticated to be authorised.

    On the other hand, that's way too complex for the large majority of applications out there. So you probably would have no issues with putting it all together in a single location. Split out objects that make sense when you will either need to do the action on multiple things (User and something else), or you need the ability to swap out objects for differing functionalities (Authenticate::passwd -> Authenticate::DBI). But that's actually not a bunch different from procedural programming, I think.

Re: The division of (class) labor in OOP
by thcsoft (Monk) on Jun 06, 2005 at 02:35 UTC
    hmm... in principle i tend to Tanktalus' point of view. it's always advisable designing classes in a way by which every class fulfills only its own purposes. but unless you are dealing with, let's say, millions of users and dozens of log files on a cluster of several computers - you'll be possibly better off with a mixed approach.
    in the applications i wrote so far i made quite good experiences with a session class, and a user class. every session object contains as one of its members an instance of user - which is either assigned to a valid user, or to an anonymous one (if she's not logged in).
    the implementation of that anonymous user helps a lot in writing your scripts:
    return FORBIDDEN if $session->user->anonymous;


    language is a virus from outer space.
Re: The division of (class) labor in OOP
by exussum0 (Vicar) on Jun 06, 2005 at 14:53 UTC
    I would suggest making your authentication stuff go into one object, but is used by the user object. It's not so much that authentication related to users or certain thigns, but because it deals with persistence usually. By keeping it as a seperate object, you can plug in a new authentication object later.

    Same thing for your display vs controller/processing logic. Suppose you wish for your display to be either html, xml or tk? If you seperate your controller stuff from your persistence (persist to screen and database), you gain some future flexibility.

    Of course, if this is not really a theory/achedemic question and you just want to get something done, do whatever is concise, and well organized. It's all matters of opinion at that point.

    Update: Separating data moving/displaying/persisting logic into own objects allow for easier mock object testing. Implementing your own test object vs doing some sorta code injection by overriding certain things is a huge pain in the ass.

    ----
    Give me strength for today.. I will not talk it away..
    Just for a moment.. It will burn through the clouds.. and shine down on me.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://463747]
Approved by Zaxo
Front-paged by monkfan
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (5)
As of 2024-04-23 20:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found