6

I'm working on a class that represents an object with multiple representations - one is an XML type representation used by an automatic ordering system, the other is a POJO-based representation used by a monitoring tool.

The problem I'm running into is that I seem to have to make trade-off between encapsulating the object's internal data and designing my application according to the principle of "separation of concerns" or "single responsibily per class".

Let's look at the class in more detail:

public class MyObject {
    private Object field1;
    private Object field2;
    private Object field3;
    ...
    private Object field100;
}

(Yes it's quite a big ugly monster, something I have no control over).

If I follow the principle of separation of concerns (the way I understand it anyway), I will have to add getters for every field in this class and have a separate XMLBuilder class for the XML representation, and a PojoBuilder for the Monitoring tool representation:

public class XMLBuilder {

    public XML asXml(MyObject obj) {
        return "<tag1>" + object.getField1() + "</tag1>"
               "<tag2>" + object.getField2() + "</tag2>"
               ...
               ;
        }
}

This seems to me to break the principle of encapsulation - I have to expose a lot of the internal data on my class just to provide a representation for it. If I pass my object to other systems, I have no control whether these sub-systems may become dependent on arbitrary fields (i.e. field45), which means that future changes to the structure of this object may become difficult as there are now other possible dependencies on it.

An alternative approach, advocated by Allen Holub (http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html) for example, is to do the representation inside the class:

public class MyObject {

    ...

    public XML asXml() {
        return "<tag1>" + field1 + "</tag1>"
               "<tag2>" + field2 + "</tag2>"
               ...
               ;
    }

    public Object asMonitor() {
        ...
    }

 }

To me, this solution seems great, as the class never exposes any private fields. The representation generators have access to all the information it needs, and the object itself can be safely passed to other sub-systems without the worry that another module would depend on internals.

Now, when I implement this, my colleagues all raise the issue of "Separation of concerns". The problem, they say, is that the MyObject class now have knowledge of things like XML representations and POJO represantions, which means that if the XML representation changes, you now have to make modifications inside the actual class itself, instead of just updating an "XML ruleset" or some other external configuration.

Holub's solution is to use a Builder pattern:

public class MyObject {

...

    public void buildContent(MyObjectBuilder builder) {
        builder.setField1(field1);
        builder.setField2(field2);
    }
 }

 public class XMLBuilder implements MyObjectBuilder {

     ...

     public void setField1(Object field1) {
         content += "<tag1>" + field1 + "</tag1>";
     }

     public XML asXML() {
         return content;
     }

     ...
 }

but this seems hardly better than the first approach - instead of exposing data as getters, it is now implicitly exposed via a builder. OK, I understand that with the builder I may have additional control over how my object represents itself, but is that iota of control worth the increased complexity in the code base? Changes to the structure of MyObject will still likely result in downstream dependencies requiring updates, so I'm not entirely sure if much have been gained here (except that there are technically no getters).

Is this a case where you simply have to choose your poison? Or is there a magical middle ground that reduces all these issues? What are the common strategies for dealing with this?

  • Sometimes you have classes, which represent the business logic, providing simple public API and hiding implementation details (be it inside the public methods or private ones), sometimes you have classes, which represent the data and are passed around in your application, which provide getters. Getters and Setters are Evil cannot and will not apply to everything. My prefered way is to have the POJO with public properties and pass it to processors which know what to do with the data. Because when I want a XML representation, I don't care about Monitor and vice versa. – Andy Dec 7 '15 at 10:22
  • 4
    I am surprised by the "duplicate" tag. The two questions seem only superficially similar (they both question the Single Responsibility principle, I guess). My question is about representations and information hiding. – firtydank Dec 8 '15 at 11:13
  • 1
    @firtydank: I agree, there are some people here who press the "close as dupe" button a little bit too quickly for my taste. I am voting for reopening. – Doc Brown Dec 8 '15 at 11:36
  • What is the difference between the object itself and the "POJO representation"? – Aaron Kurtzhals Dec 8 '15 at 20:29
  • @Aaron - the monitor application has its own API which uses a different object domain. – firtydank Dec 9 '15 at 7:30
9

Is this a case where you simply have to choose your poison?

Yes, basically.

One of software developer's job is risk management when it comes to change. And there is lots of theory about risk management. Generally, you should first identify risks. You already did that : change of object itself, change of XML, change of POJO, etc.. All of those are possible risks. Next step would be to identify what are probabilities of the occurring. You can get some idea from looking back at changes you had to do in the past, but only real experience can help you here. Then, you have to identify impact of each risk. You say that changes can propagate through the dependencies, so the more dependencies the risk affects, the more costly it is when it occurs. Taking all of this in, you should make a design that minimizes risks that have high probability and high cost.

I can't tell you right now, because the way you described your problem is way too generic. And different systems would encounter different risks at different probabilities and costs. For example, if XML is used as communication between your systems, then updating it's schema might not be a problem. But if it is used for persistence, then maintaining backwards compatibility is a must. Etc..

Only thing I can suggest is to get more experience. More experience will give you more ideas about possible risks and their properties and ways to design to avoid those risks.

2

This seems to me to break the principle of encapsulation - I have to expose a lot of the internal data on my class just to provide a representation for it.

Not really. In one approach, you expose data and structure through 100 getters, in the other, you expose essentially the same data and structure through an XML with 100 tags. From the viewpoint of dependencies and encapsulation, there is no (!) difference between these two solutions.

Moreover, you should consider not to implement this "manually". There are some reflection based libraries (like XStream) which can do an Xml serialization / deserialization for you (some more are mentioned in this former SO post). They work fine with private attributes, so no need to write any additional getters and setters. Using such a library will allow you to stick to the SRP - your object will not know anything about the serialization format.

If you really cannot use one of those "out of the box" XML serializers, you may consider to implement this based on reflection by yourself. That will surely safe you from writing the same kind of boilerplate code again and again.

If you do not want to use reflection and go the route of writing similar code for each individual attribute, consider to provide a method asMap in your class MyObject which delivers you a more or less "format neutral" representation of your object like a Map<String,String> (with the field names or tag names as keys). Your XmlBuilder then can take this as input and reformat the map into the final xml format.

  • I think there is a difference between exposing all your internals to whoever can abuse it and providing a representation for a well-defined integration point. – firtydank Dec 8 '15 at 11:03
  • @firtydank: maybe you misunderstood my answer, see my edit. – Doc Brown Dec 8 '15 at 11:31
  • Hmmm, still not convinced. At least you have to admit it is much less likely for a downstream developer to invoke your XML representation in order to get to your internals, and even then he will only have access to the internals related to that specific XML representation. With the getter approach, he has carte blanche on everything. – firtydank Dec 8 '15 at 11:40
  • @firtydank: it is the same with "getters": you provide some data through them, but your private implementation might look differently, there is no rule saing each getter must map to one private member - similar to the XML. And encapsulation has nothing to do with convenience - if "getters" and "XML" expose exactly the same data and structure, you have the same level of encapsulation, even if one form of access is more convenient than the other. – Doc Brown Dec 8 '15 at 11:58
  • True, however, it seems to me to be general practice to expose the getters "as-is" and then leave it to the various representation generators to do format-related processing and filtering, especially in cases where a single domain object may have multiple representations. – firtydank Dec 8 '15 at 12:04
1

Some serialization frameworks implement following approach:

  • Each model object is required to implement a certain interface
    • Interface accepts an abstract visitor-like container object
    • Model object is responsible to populate container object
    • Container provides trivial operations like storing key-value pairs, sequences, nested objects, may support storage of all primitive types
  • There are multiple implementations of containers, one for XML, one for POJO, one for JSON etc

This approach reveals structure of model object, but makes it inconvenient to abuse.

See boost::serialization library for reference.

Here is my simplified draft for Java:

public interface Container implements Closable {
  // Repeat for all primitives
  void put(String key, String value);
  Container getChild(String key);
  //TODO: add sequences support
}

public class ConcreteModelObject {
  private final String datum;
  private final String id;
  private final Child child;
  private class Child {
    private final String childId;
    void store(Container container) {
      container.put("id", childId);
    }
  }
  // The only structure exposure point
  public void store(Container container) throws IOException {
    container.put("id", id);
    container.put("datum", datum);
    // Child container should be closed after use, to allow closing of XML tags, for example
    try (Container childContainer: container.getChild("child")) {
      child.store(childContainer);
    }
  }
}

This approach lowers coupling between serialization and model object, but does not eliminate it. Similar results can be achieved by choosing a most potent serialization method, implementing that in model, then convert resulting format to others (for example, make model be serializable to XML, provide a way to convert XML to JSON).

Deserialization is more or less symmetric. Also note, that this approach can be memory consuming if implemented wrong. See boost::serialization for optimized solutions.

  • Yes - if you make the Container typesafe with setters, then you essentially have the builder used in option 3. What I like about this Container/Builder pattern is that it gives you a measure of control over trade-off you are making - which I think comes back to what @Euphoric is saying as well. – firtydank Dec 11 '15 at 8:04
-1

Exposing the properties as public and having separate classes do the XML representation is a pretty standard pattern. There are lots of XML/JSON etc libraries out there for converting class objects to various data representations, and your code is going to be most useful if you leave it compatible with all of those.

And for what it's worth, I don't really see the difference in terms of third party dependencies. If it is going to be part of the XML representation, then it is part of the public interface and should be public.

  • 2
    No, I can't agree that just because something is needed in a specific XML representation that it should be available to everyone else too. – firtydank Dec 8 '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.