in reply to Re^4: Re-blessing || Re-constructing objects
in thread Re-blessing || Re-constructing objects

Have the user object contain an authorization object. Then, when the userobj wants to know if it's allowed to do certain things, it asks the authobj. The authobj replies with a boolean yes-or-no.

The reblessing you want to do is really changing the authobj. So, just change the authobj! There's no need to get all fancy. In fact, a good rule of thumb is "If I have to ask if something is possible on Perlmonks, I probably should rethink my design." About 90% of the time I ask a question, my design changes. This is a good thing.


My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
  • Comment on Re^5: Re-blessing || Re-constructing objects

Replies are listed 'Best First'.
Re^6: Re-blessing || Re-constructing objects
by blogical (Pilgrim) on Apr 18, 2006 at 03:03 UTC

    This sounds like compartmentalizing the aspects that might vary (method results) into a seperate object (authobj). Then, if you need to change how something works, you swap out the authobj for another one.

    That sounds like more work to me, and for what? The method variations still have their own spaces, seperate by class lines, but you're suggesting using multiple objects where I only need one. If I needed authobjects for other types of objects I could see that being a compelling argument (design the piece, then plug in where needed). But it seems like a smaller change to start using a different name (rebless) for an object than to throw away part of it, create a new part, and plug it into the hole.

    For clarification of my motivation, I'm not asking if this IS possible (I know it is, conceptually and in practice) but why ELSE it might be desirable (or undesirable.) I'm certainly not afraid to change my ways- if I was I wouldn't be re-implementing my current project with OO code after months of development. I'm also open to accepting I am misguided- but it takes a clear argument tthat puts me back on course to convince me.

    "One is enough. If you are acquainted with the principle, what do you care for the myriad instances and applications?"
    - Henry David Thoreau, Walden

      The key concept you're not grasping is the fact that change is something that needs to be compartmentalized because it is the only safe way to build a large system. That system could be a piece of code or a bridge. You have to isolate change or that change will percolate throughout the entire system willy-nilly. In the case of a bridge, the bridge falls down. In the case of a piece of code, this means that every bugfix and/or enhancement might change the behavior of any part of the system, especially those which weren't touched.

      What does this mean? Well, under your proposed system, it means that by fixing a bug in how User::Superman behaves, you might have changed the behavior of User::Anonymous. I'm pretty sure you would agree that this is undesirable.

      The solution is to isolate the part that changes. Every user needs a set of authorizations, but that set (as a whole!) will change. So, isolate (or encapsulate) the bit that changes.

      It is a tiny bit more upfront work. Yet, you will discover that you have more capabilities to the system than if you gone with your system. It's a bit of a leap of faith, but I hope you will trust me and be willing to make it, for your sake.


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

        This does seem to be sound advice, and I appreciate everyone for pointing it out. I think the key is recognizing what level of compartmentalization is appropriate for a particular situation.

        Public object methods are a widely recognized contract that, without warning well in advance, the effects of calling a method will not change. The safety of compartmentalizing comes from that contract alone, not the form of the compartment. If I take any space and give it the same contract, it becomes a compartment of just such strength. By doing so you make available compartments of different shapes and sizes, from a simple scalar to the most complicated data structure. The line of delineation is no longer what one might expect- until they read the documentation that details it.

        It is the compartmentalization, not how you effect the compartmentalization, that makes it useful. Does it make sense to re-use common, publicly acknowledged contracts to do so? Yes (see Creative Commons). Is it wise to follow that as a guideline unless you have good reason to do otherwise? Sure. But might there be uses that don't follow the guidelines? I believe so. Hence, this entire thread.

        "One is enough. If you are acquainted with the principle, what do you care for the myriad instances and applications?"
        - Henry David Thoreau, Walden

      It is more work. It also gives you abstraction between the classes. You get to use the public API, rather than having to know all the internals in multiple places. Think of the poor guy who comes along in a year and has to change something about the implementation of the AnonymousUser class, only to discover that the AdminUser class is now broken on some other part of the site. What a debugging nightmare.
Re^6: Re-blessing || Re-constructing objects
by Juerd (Abbot) on Apr 18, 2006 at 10:41 UTC

    Have the user object contain an authorization object. Then, when the userobj wants to know if it's allowed to do certain things, it asks the authobj. The authobj replies with a boolean yes-or-no.

    But then you have an ugly set of

    sub method1 { ... if ($self->authenticated) { ... } else { ... } ... } sub method2 { ... if ($self->authenticated) { ... } else { ... } ... } sub method3 { ... if ($self->authenticated) { ... } else { ... } ... }
    instead of a nice set of
    package User::Anonymous; sub method1 { ... } sub method2 { ... } sub method3 { ... } package User::Authenticated; sub method1 { ... } sub method2 { ... } sub method3 { ... }

    I think checking inside the methods is (security) error prone, and a lot more work, resulting in uglier code that is harder to maintain. Besides that, it's less efficient because of the extra check (which if you abstract properly, is a method call). I prefer doing something that isn't entirely pure in the sense of "how OO was officially meant" (if such a thing even exists) to something that is pure, but causes me and the future maintenance guy extra work. And OO purity is a performance killer in many ways, which is a problem if you happen to work for one of those shops that still care about that.

    So you can't easily subclass anymore. Fine with me, because you can still wrap. If I properly designed my User, I'd let you add arbitrary states like Anonymous and Authenticated, and you could change things through that interface. Personally, I don't think inheritance is holy enough to have as a primary design objective. Composition is more useful IMO, and reblessing to change state (combined with inheriting (or mixing in) from a state independent class) is a primitive way of doing that.

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

      Yes. However, I tend to put my authentication code in my cgiapp_prerun and, very rarely, refer to it in other places. This reduces work quite dramatically.

      I am advocating a composition solution in favor of an inheritance solution. Are we just in violent agreement here?

      As for not subclassing as easily, it's the reblessing solution that's harder to subclass. Not only am I restricted to a given implemention, but I have to support every single key used by every version of those other implementations. What a nightmare!


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

        Yes. However, I tend to put my authentication code in my cgiapp_prerun and, very rarely, refer to it in other places. This reduces work quite dramatically.

        In the case of only authentication, that's a perfect solution. However, sometimes the authentication leads to authorization, and you have a system of user rights scattered all over the place. Instead of having many conditionals, I typically prefer to put all functionality in methods, and indicate rights by (re)blessing into the right package.

        it's the reblessing solution that's harder to subclass.

        That is what I referred to.

        Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }