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.

Suppose I have design like this:

class Corpus:
    def something1(self):
        pass
    def something2(self):
        pass
    def serialize_to_stream(self, stream):
        _CorpusJsonSerializer().serialize(self, stream)

class _CorpusJsonSerializer:
    def serialize(self, corpus, stream):
        .
        .
        .
    .
    .
    . 

I think that the serialization process should be in a separate class for various reasons. That makes the method serialize_to_stream in Corpus unnecessary. I would however like to have it there because I think it makes it easier for the users of the Corpus, because they do not have to know anything about another class CorpusJsonSerializer.

Do you think it is reasonable? Or shall I just expose the CorpusJsonSerializer and let the user do the work?

share|improve this question

1 Answer 1

up vote 2 down vote accepted

This is a trade-off between ease-of-use and decoupling.

Having a serialize-method part of your Corpus class may make it easier to use, but this way your Corpus class depends on _CorpusJsonSerializer and vice versa, which means you have a cyclic dependency. This is often a sign of bad design, since it makes Corpus harder to test, harder to reuse in a different context, harder to extent etc.

In most cases, I would avoid solutions with cyclic dependencies. Even better would be a design where you don't need a special _CorpusJsonSerializer, but only a general JsonSerializer which takes an arbitrary object (for example, of type Corpus) and determines its type and the serializable attributes of that type at run time. This way, you would eliminate all of the current dependencies.

Another design option, which is not so generic but avoids cyclic dependency, is to inject the dependency from outside. Means, in the Corpus constructor provide a parameter for a "serializer" object which is stored in your Corpus object and used in the serialize_to_stream method. You can pass here an object of _CorpusJsonSerializer in most cases, but also a completely different object following the same interface conventions (i.e. providing a serialize method). This way, when for testing purposes you don't need a serializer, you don't have to provide one; or if you need a different kind of serializer, you can easily exchange it. Of course, this option will imply that _CorpusJsonSerializer is exposed to the user, but you can keep the convenience method in Corpus without the drawbacks of cyclic dependencies.

share|improve this answer
    
Didn't spot the cyclic dependency sorry... –  Chedy2149 Aug 19 '13 at 10:28
    
I do not want to build the general JsonSerializer because I will not need it. I know that I need to serialize just this one concrete class in my project. I also don't like the DI in this case. If I would have already exposed the _CorpusJsonSerializer I would probably abandon the convenience method completely because then it would have not enough added value. I was just trying to isolate the serialization behaviour from the Corpus so the class isn't too large. Do you find the circular dependency bad even in this concrete case where one of the classes is in the same module and is private? ... –  Honza Brabec Aug 19 '13 at 11:52
    
... but I admit I've asked the question with the interest to the general case and for that you have replied well, so +1 –  Honza Brabec Aug 19 '13 at 11:54
1  
@HonzaBrabec: I think you have the correct decision already made up in your mind. For a very small project, with just one class to serialize, the circular dependency may be acceptable (as long as it does not hinder you from creating any of the tests you may intend to write). But many projects tend to evolve, so be sure you don't miss the point where you have to improve the design. –  Doc Brown Aug 19 '13 at 12:00

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.