1

Today I received a rejection letter from a company where as part of the interview process they gave a a couple days to respond to the following question (paraphrased):

Let’s say say you have instances of class UserReview and need to save them somehow, using a storage service of some sort.

Please provide pseudo­code, draft code or a clear description that indicates how you would save these UserReview objects to a storage service in such a way as to allow:

  1. The storage service to be changed at some unknown point in the future, for example, from an in-house service to a cloud service, minimizing impact on the Android code ­ particularly avoiding changes directly to the UserReview class itself. (Don’t worry about the API interface to the remote store unless that is key to your solution.)

  2. The design pattern(s) adopted and implemented for UserReview to be leveraged, minimizing code duplication, to allow other objects (e.g. a new class called SellerResponse) to be saved in a similar way.

Key to our interest in this problem is how you would accomplish these two goals efficiently and cleanly, maximizing Object Oriented reusability and flexibility while avoiding over­designed code and the Android design patterns and language features that you would use to do so.

I responded to the question with code using the following interface:

//To add a new Remote Store, just implement a Saver class for it
//and it will work with all the other classes that use Saver.
public interface Saver {
    //Ultimately, every Object should be representable as a series of bytes,
    //and every data store should be able to store bytes. In the worst case
    //we can use base64 to encode Objects as strings.
        SaverResult save(InputStream data);
    }

//This is one way of allowing code that uses Savers to respond to errors/unavailability.
//Of course, we could also subclass Exception and then throw it, but this way allows us to
//easily chain multiple save requests without multiple nested try-catch blocks.
static class SaverResult {
    public final boolean failed;
    public final String errorMessage;

    SaverResult(boolean failed) {
        this(failed, null);
    }

    SaverResult(boolean failed, String errorMessage) {
        this.failed = failed;
        this.errorMessage = errorMessage == null ? "" : errorMessage;
    }
}

I also included some examples of using the Saver interface including an example of Dependency Injection, (that is, a class with a constructor like this:public UserReview(String review, String username, long id, Saver saver)) and an example of keeping the Saver instance outside the object to be saved, and showing how that allows saving the object to two different services easily. For example:

s3.save(Common.toInputStream(review1.toString()));
inHouse.save(Common.toInputStream(review1.toString()));

Was this a good way to answer the question? Is there a better way? Was there something important I missed?

I'm asking not just so I can do better on future interviews, but also because I want to know if there's a reason this wouldn't be a good solution if the problem actually came up.

  • 3
    You're going to get the best feedback from the company that you submitted this to. We have no way of knowing what the interviewer considers "good" and "better." In a perfect world, we'd all agree on these things, but often interviewers are expecting a specific answer, and if you don't give them that answer, it doesn't matter how "good" your response is. – Robert Harvey Feb 23 '17 at 1:02
  • You seem to have tried hard. Honnestly, I would have, in real situation just shove a one liner save() in the UserReview that would save the object, until i actually got to that point of the future where we need to change something. I have no idea why hiring people ever believe a complex solution is the best way to answer a simple problem "in an flexible and extensible way". My manager prefer to say "Code quality sounds cool, but when do we ship actual features ?" which I agree a lot more with. – Arthur Havlicek Feb 23 '17 at 5:52
  • @ArthurHavlicek: yeah, and, the devil is always in the details, and it's pretty much impossible to make a "obviously correct" solution to such a vague requirement. – whatsisname Feb 23 '17 at 7:05
  • @gnat I considered asking this on Code Review or Workplace, but this seemed like the best place. I had hoped that since I'm also interested in knowing whether this actually is a good design or not, as the question's final sentence indicates, this would be considered sufficiently on topic. – Ryan1729 Feb 23 '17 at 8:31
2

What they are basically asking you is to implement and describe a Repository pattern.

I think the thing that shot the whole solution down is this :

Ultimately, every Object should be representable as a series of bytes, and every data store should be able to store bytes.

This is totally, completely, utterly incorrect. If, for example, you were storing objects into a relational database, you would want each field of that object to be a single column. Saving only Base64 string for each object would drastically limit indexing and querying. Another problem with this is in what format the binary data would be? If code uses some kind of proprietary binary serialization, then it would be complicated to query the data from different place.

UserReview(String review, String username, long id, Saver saver)

All domain objects should have no idea how they are saved. This is close (but not exactly) an active record pattern. It is by some, considered an anti-pattern, and thus bad design.

0

There are several ways to design this system, and nobody knows what the company was specifically looking for. What I can do, however, is show you my thought process and how I would design it.

First of all, the theme here is decoupling of data objects and services. We should be able to serialize an object through any service, and each service should be able to serialize any data object.

There are two primary ways to achieve these goals in Java.

First of all, I need to point out that the problem statement makes no mention as to any constraints on the data objects - are they POJO Java beans? Are they more complex? I have no idea.

Interfaces

We could tag each data object with an interface that marks it as being acceptable to save to a service. Java already includes one such interface, java.io.Serializable. We could reuse that.

Services would likewise have an interface that specifies they can serialize objects: perhaps a public void serialize(Serializable obj) method. Note that there is no mention of deserializing, or loading, objects.

This is the more traditional, "old school" approach.

Annotations

The way I would implement this is via Java bean annotations, preferably through a framework such as Spring.

This has some serious advantages:

  • No interface dependencies. The "interface" is documented via annotations (or configuration files such as XML or properties), placing few constraints on existing code.

  • Instead of wondering how you will locate a particular bean (data object) definition or service, you ask Spring to locate it for you. This means you write much less code to "glue" your code to external definitions of data objects or services. You give up some compile-time static type safety for excessive configuration and delegation at runtime, which actually ends up being better: it is now possible to drop in jar files with services and never have to think about how to figure out what they are. Spring's annotation processor finds them for you.

  • Ease of use. Make a call into Spring to get a particular object, and in one line of code you can get it. You do not need to dig into factories or other patterns (which build on other patterns, and end up giving you a class like BeanSerializationServiceLocatorAbstractFactory which is literally the butt of Java jokes).

I am not an Android expert but I do believe it has similar facilities built-in.


I am not going to give you any code: hopefully me describing my process will help you more. Perhaps you should build a test project to learn about how to make this work, and ask SoftwareEngineering.SE for a design review.

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.