5

In my spare time, I've been designing a CMS in order to learn more about actual software design and architecture, etc.

Going through the SOLID principles, I already notice that ideas like "MVC", "DRY", and "KISS", pretty much fall right into place. That said, I'm still having problems deciding if one of two implementations is the best choice when it comes to the Single Responsibility Principle.

Implementation #1:

class User
    getName
    getPassword
    getEmail
    // etc...

class UserManager
    create
    read
    update
    delete

class Session
    start
    stop

class Login
    main

class Logout
    main

class Register
    main

The idea behind this implementation is that all user-based actions are separated out into different classes (creating a possible case of the aptly-named Ravioli Code), but following the SRP to a "tee", almost literally.

But then I thought that it was a bit much, and came up with this next implementation

class UserView extends View
    getLogin //Returns the html for the login screen
    getShortLogin //Returns the html for an inline login bar
    getLogout //Returns the html for a logout button
    getRegister //Returns the html for a register page
    // etc... as needed

class UserModel extends DataModel implements IDataModel
    // Implements no new methods yet, outside of the interface methods
    // Haven't figured out anything special to go here at the moment
    // All CRUD operations are handled by DataModel 
    //   through methods implemented by the interface

class UserControl extends Control implements IControl
    login
    logout
    register
    startSession
    stopSession

class User extends DataObject
    getName
    getPassword
    getEmail
    // etc...

This is obviously still very organized, and still very "single responsibility". The User class is a data object that I can manipulate data on and then pass to the UserModel to save it to the database. All the user data rendering (what the user will see) is handled by UserView and it's methods, and all the user actions are in one space in UserControl (plus some automated stuff required by the CMS to keep a user logged in or to ensure that they stay out.) I personally can't think of anything wrong with this implementation either.

In my personal feelings I feel that both are effectively correct, but I can't decide which one would be easier to maintain and extend as life goes on (despite leaning towards Implementation #1.)

So what about you guys? What are your opinions on this? Which one is better? What basics (or otherwise, nuances) of that principle have I missed in either design?

1

3 Answers 3

5

I've always considered the single responsbility principle more of a philosophy than principle.

Robert C. Martin (Uncle Bob) restates the Single Responsibility Principle (linked to PDF):

There should never be more than one reason for a class to change

Quoted from the reply you got your post.

When we try to appease the SPR, we focus on the implementation rather than on the purpose.

Major mistake.

My understanding of the SRP is this, when an addition blurs the responsibility of an implementation, the focal point should be reconsidered.

For me, it is all about the relationships between purposes.

Sure, a user has to be authenticated and authorized regarding actions to take - but the authentication and authorization should be separated from the user.

It's not like you are going to try to authorize/authenticate a non-user anyway?

And thus, you should consider splitting implementation when the responsibilities of a class outgrows it purpose. After all, design patterns have purposes too.

Take a look at GRASP when you get a chance.

2
  • I've looked at GRASP before, and it's probably what really drove me to design Implementation #1. Though I am wondering, in keeping with what you said about splitting implementation, should I spread out the MVC architecture, instead of over User like I did in #2, but over all the modules (Login, Logout, etc...) in the user 'space'? i.e. LoginControl, LoginView, etc, etc... ?
    – Mike S
    Nov 10, 2011 at 11:16
  • SRP is basically function spaghetti in OOP clothing as far as I'm concerned. At best, it works for the interior of composite classes but it fails at intuitive easy-to-use interfaces in higher level objects. Higher level classes should represent problem domains, not functions with persistent vars. Excess separation of concerns before it was needed has been my #1 problem with the majority of legacy codebases I've inherited. Ultimately it's a violation of YAGNI and IMO probably originally a broader ideal turned into an antipattern to get that 'S' in SOLID. Jul 17, 2013 at 19:09
1

"Single Responsibility Principle" is not the same as "Single Functionality Principle"

Many a times - when we try to do this as religiously as possible - we tend to "break" classes a bit too much and we may still violate SRP somehow because now each object might need to know something about the other that becomes an extra reason for change.

I am not very clear about the intention of above classes but i guess here are few points -

  • I don't know if login and logout can be truely independent of each other and whether either of them can be independent of session

  • I don't know whether you would regard login and logout as classes or methods and why.

  • I don't know whether UserManager doing create is any different form new user()

I guess i am completely wrong in many things because i have not truly understood the entire thing. The second one is a little better than first but i may still be wrong.

But that is precisely the point - by looking at the classes the intentions are note very clear - the sign that SRP is not quite working here.

1
  • Yours is the only answer that's really making me think about my implementation, and I really appreciate that. It's refreshing. I'll think about this a bit more concerning what you brought up about the intention of classes rather than the responsibility and see what I can come up with. I may end up posting that refactor up on Code Review, since that's a more appropriate place to ask this kind of question. Thanks a bunch!
    – Mike S
    Nov 12, 2011 at 14:57
0

One way I like to do this is to start building your classes ignoring your entire structure.

And then follow a general rule of 3.

  • If you end up with more than 3 public functions, break them up into separate classes.
  • If you end up with more than 3 internal variables, break it up into separate classes.
  • If you end up with more than 3 functional lines in your function, separate it into different functions.

This way you don't have to angst over whether or not you have your structure correct. You can be fluid with your development. This process of coding is easier if you have unit tests or do TDD in general, but it is not 100% required. It also removes some mental overhead for your brain.

I find I will sometime worry so much about the structure and agonize over it so much, that as time goes on, I'm too attached to the structure and I'm less willing to adapt when I need to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.