views:

88

answers:

4

I write a URL router in Python 3.1 and wonder whether it is more than a matter of taste to use one of the following variants:

Tuples as constructor params:

router = Router( 
    (r"/item/{id}", ItemResource()),
    (r"/article/{title}", ArticleResource())
)

Method calls

router = Router()
router.connect(r"/item/{id}", ItemResource())
router.connect(r"/article/{title}", ArticleResource())

Do you see any advantages or disadvantages here?

A: 

Unless the parameter are mandatory, you could use the method call approach which would be more flexible if you need to add more in the future.

If you must use both parameters for your instance to be valid, use the constructor approach.

Rod
+2  A: 

The traditional OOP view is that the constructor should guarantee that an object is in its final, usable state -- that is, the state may change, of course, if there are dynamically changing requirements (e.g. is there a disconnect method to go with that connect, and an actual app requirement to enable the dynamic changing of routing in the course of operations?), but it "should" stay usable from the moment the object is born, to when it goes away.

In the real world, the alternative pattern sometimes known as "two-phase construction" (though here it might be more than just two phases of course, as you keep calling connect;-) may have some advantages in terms of flexibility -- persisting and de-persisting objects, building them right dependently on configuration files, easing dependency injection and mocking (and therefore testing).

Whether you're actually going to take advantage of these flexibility aspects, is a question you can answer better than we can... since you best know your app's exact requirements and your own skill and preferences in OO development, testing, &c. If you're not, in fact, going to use the flexibility aspects, then it's simpler to omit them altogether, and go with the "traditional self-sufficient constructor" OOP approach.

Alex Martelli
There is nothing in OOP that says all objects are read only, and in their final state after construction. Usable yes, but an empty routes table is usable though not very interesting.
stonemetal
@stone, yep, as I said, "the state may change, of course" (otherwise we'd be taking about FP, not OOP;-).
Alex Martelli
A: 

Since you are building a router to route web request to appropriate resource, you can also look into Routes. It allows you to map URLs to your application’s actions.

pyfunc
Thanks, I know routes, but it doesn't fit my needs.
deamon
+2  A: 

I favor passing the tuples to the constructor for two reasons that Alex didn't mention (at least not explicitly).

  1. Readability. If is plain to any reader of the code that the Router instance requires a list of routes to be usable.

  2. Lose coupling. Client code does not have to bother with initializing Router instances. This is the responsibility of the Router class.

If this is feeling bulky to you, I would recommend breaking the Router class into smaller classes and passing instances of those classes to Router.__init__. For example, here you could have a RoutesList class:

routes = RoutesList((r"/item/{id}", ItemResource()),
                    (r"/article/{title}", ArticleResource()))

router = Router(routes)

This buys you the flexibility of the two phase construction that Alex mentioned but also prevents client code from being responsible from initializing the router class. This still preserves readability because you have to read backwards to see what routes is and not forward (if you don't happen to remember). It has the added advantage that if you should decide to change how you are representing your routes, the Router class shouldn't have to change at all: all such changes should be encapsulated in the RoutesList class (or perhaps even in an as of yet undefined Route class).

Or routes could just be a list of tuples and you could have a module level function to map the route to the controller ;)

aaronasterling