3

Let's assume I have an object a of a class A. It has a method that needs an argument in form of another object of a particular type - but the argument should also be in particular state, because the operation performed by the method (let's say it's querying an external service, but it could be something else) requires it.

class A(object):
    def action(self, argument):

        return do_something(argument)

The first solution that comes to my mind is simple: the method should enforce a condition on the input data. It should test it for required state and then either raise an exception or modify the state:

class A(object):
    def action(self, argument):
        if argument.state == False:
            raise Exception()

        return do_something(argument)

class A(object):
    def action(self, argument):
        if argument.state == False:
            argument.change_state()

        return do_something(argument)

However, the instance we are talking about is only one of many instances of the A. All these instances should perform the same operation on input data: testing it for a particular state and then enforcing it, or just raising an exception. It seems they could use a shared instance of parameter whose required state is already enforced by an external caller, instance of class B:

class B(object):
    def action(self, argument):
        argument.change_state()
        for a in self.instances_of_A:
            yield a.action(argument)

Because I think the operation performed by the method wouldn't be properly encapsulated without enforcing the required state of argument inside the method, I think it should be moved to the former caller. The object a and all other instances of class A would just provide data necessary for performing the operation.

class B(object):
    def action(self, argument):
        argument.change_state()
        for a in self.instances_of_A:
            yield  do_something(argument, a.data)

However, there is another problem: the operation returns a value that could be interpreted in a way related to instance of A to get another piece of data - and now it seems like the operation do_something should be encapsulated in a method of class A.

How should I solve this problem? Should I simply keep the current design and make object a return data that could be used to interpret return code provided by the operation? Perhaps I should use chain of command pattern and modify class A to delegate the operation to other instances of A? What are the other ways I could handle it?

  • 5
    I think your question would be a lot better with a real example instead of A and B. As it is, I can't make heads or tails out of what you are doing. – Winston Ewert Nov 22 '15 at 5:47
  • 1
    I agree that we'd need to know what A, B and do_something() do and what values they operate on and return in order to answer this. Maybe you should get rid of B entirely in favor of an array of A's that you map() lambdas on. Maybe the "getting another piece of data" should be a method on A. Maybe do_something should be a method on B. Maybe the values returned by do_something should be classes with methods that do some of this stuff. Until I understand what this code is actually supposed to do, I have no idea which of those is right. – Ixrec Nov 22 '15 at 19:01
  • Does needs to be in a particular state mean has to be set up correctly? Then you probably want a builder. – tkausl Nov 23 '15 at 9:36
1

I guess you need to introduce a simple DTO into your scheme. Take your agrument, check if it is in a valid state and put that state into a new DTO object. In your last example, you can do that inside your B::action method.

This way you will no longer need any state checking because this DTO is always in a valid state by definition. A will enforce its contract by accepting only this DTO for an operation.

|improve this answer|||||
  • 1
    A wrapper may be a solution, but probably not a DTO. As the linked article states, "[t]his pattern is often incorrectly used outside of remote interfaces. This has triggered a response from its author where he reiterates that the whole purpose of DTOs is to shift data in expensive remote calls." – outis Nov 23 '15 at 10:26
  • I used DTO term, probably, incorrectly, just to indicate, that the state should be put into a new object, created just for this purpose. You may call it a wrapper or whatever, of course. For me, DTO is a simple object, created for the sake of carrying data. Be it a remote transfer or not. – Vladislav Rastrusny Nov 23 '15 at 11:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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