Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I have a complex object (call it BusinessLogic) which provides an RPC interface to semi-trusted users. The functions in the RPC interface have to decide which procedure to call, check authorisation for that function, perform some argument checking, and then call private functions to carry out the required functionality, and then return results to the caller.

I decided to implement this by having the RPC functions in a separate stateless object (call it AppInterface), to separate out the concerns somewhat, so that BusinessLogic just moves its own data around and AppInterface decides which methods on BusinessLogic to call to implement the functionality it exposes.

This works very well for the most part. However it feels a bit wrong to me. The BusinessLogic object has benefited from not having to worry about authorisation, argument checking, etc, which is good. But since the AppInterface implements all of its functionality in terms of BusinessLogic, it can just be a thin wrapper for some procedures (which makes me question its necessity) and is a complex wrapper in other procedures (which makes me wonder whether that complexity should be pushed back into the BusinessLogic, given that the BusinessLogic has all of the information needed to perform the task). It also means that AppInterface needs access to methods in BusinessLogic that would otherwise be private as nothing else uses them; think friend functions in C++. Finally, it means I can't effectively unit-test AppInterface without using a full BusinessLogic object as that is the only practical way to get valid output.

I am happy to leave the system as it is, as it works, but I am curious as to whether there are obvious improvements I can make to such a system.

(For what it's worth, I am using Python 2.7, in a primarily object-oriented style.)

share|improve this question
add comment

5 Answers

up vote 5 down vote accepted

You're describing a layer relationship more than a coupled relationship. Unless your BusinessLogic depends on and makes calls into AppInterface, it's not coupled. Having a lot of dependencies on the layer below you is quite natural and unavoidable. You don't worry about how much your BusinessLogic depends on a database layer, do you?

However, the fact that some calls are thin wrappers and some are thick leads me to question the reasons why some are so complex. If it's just a different-looking interface to a BusinessLogic function, that should be moved into BusinessLogic. If it's because some arguments are more difficult to validate than others, you should figure out a way to separate that concern. Same goes for authorization.

share|improve this answer
    
BusinessLogic makes a single call into AppInterface to initiate the RPC processing but that is a simple and well-defined interface (a bit like the visitor pattern saying "call this with me as the argument"). Your second point is pretty much what I was thinking - the complex logic should probably be moved into BusinessLogic and AppInterface should just call that instead. –  Kylotan Feb 29 '12 at 14:52
add comment

Having coupling between two objects is not good. Anyone would vouch for it. However, i think two coupled objects still makes it better than one monolithic objects any day.

The object split allows simplification on at least how the code is managed even if they are not truly independent to evolved.

As far as your elements are concerned, i felt the best bet is if you can successively reduce the back dependency of AppInterface over Business Object. A simple process that allows you to think that way, is to think of another Business object that needs to access the same AppInterface. Try to keep reasonably unique and different behavior/expectation of the second BO that will force to AppInterface to be as general purpose as possible.

You should never merge AppInterface back inside Business object, but try to make it as generic as possible.

share|improve this answer
    
The structure of my system is such that there will only ever be a 1-to-1 relationship between the BusinessLogic and the AppInterface, as the interface is designed specifically to give access to a subset of this logic. So I agree with your suggestion on general grounds, but I don't think it applies to my case. –  Kylotan Feb 29 '12 at 14:55
add comment

Every object needs coupling. What you want to avoid is tight coupling. Does your BusinessLogic object break encapsulation, or depend upon methods being called in a specific order? If not, then it exists in a fine abstraction state.

Does the AppInterface depend too much upon the details of how the BusinessLogic object functions? Then you need to redesign AppInterface. Note that depending upon the agreed upon return values from functions is correct.

For your unittests, just properly document what "should" be returned by method calls, and build a MockBusinessLogic object. All the same methods, with only a return statement.

share|improve this answer
    
For the most part AppInterface only relies on BusinessLogic functions - but occasionally there is no public function for what it needs to do, nor will any other object ever require there to be such a public function. So for now I have AppInterface access the private functions and objects to do what it needs to do, as if they were 'friend' functions in C++. Is that worse than exposing new public functions that no other object should use? I don't know. –  Kylotan Feb 29 '12 at 14:44
    
@Kylotan yes, it is. If they are necessary, then either there should be a public function on BusinessLogic that wraps all of the internal functions together for AppInterface, or there should be a public functiona allowing access. –  Spencer Rathbun Feb 29 '12 at 14:52
    
Yet making it public implies that any caller can use the functionality, but that is not the intent. –  Kylotan Feb 29 '12 at 14:57
    
@Kylotan then that logic needs to be internal to some method that can be called externally. BusinessLogic knows when that internal method is needed, so have it figure that out. Don't break the encapsulation by having AppInterface tell BusinessLogic something it should know. –  Spencer Rathbun Feb 29 '12 at 15:10
add comment

I think you should combine these objects and look for a different way to divide them, if your objects are that tightly coupled they shouldn't be separated that way and its just going to confuse future programmers why object A and B always have to be used together to get anything done.

share|improve this answer
add comment

You could refactor the public interface of BusinessLogic into a separate base class IBusinessLogic, which provides all functions needed by AppInterface. When creating the AppInterface instance, pass it a IBusinessLogic object, which in your running system will be an instance of BusinessLogic.

For testing purposes, you are able to create a BusinessLogicMock class, which inherits from IBusinessLogic, too. Now you can easily unit-test AppInterface without providing a full-blown BusinessLogic object. This may be the form of decoupling you are looking for.

share|improve this answer
    
BusinessLogic is very big, constantly changing in line with business needs, and is ultimately the decider of what goes into AppInterface, not the reverse. Abstracting out an interface would probably be too much work given that it will be constantly evolving and that only AppInterface (and any tests) will ever depend on it. –  Kylotan Feb 29 '12 at 14:46
    
Well, you asked for the road how to decouple the two objects and make AppInterface unit-testable - that's exactly the way to go. Another idea (don't know if this is possible in your case, however), is to annotate the methods of your BusinessLogic class in some way (the ones you want to have a pendant in AppInterface) so that you can generate AppInterface (or parts if it) in a generic manner from the annotations. AFAIK Python supports dynamic creation of methods which may be an option in your case. –  Doc Brown Feb 29 '12 at 17:45
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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