Class Design – How to Warn Other Programmers of Class Implementation

class-designimplementationsprogramming practicesteamwork

I'm writing classes that "must be used in a specific way" (I guess all classes must…).

For example, I create the fooManager class, which requires a call to, say, Initialize(string,string). And, to push the example a little further, the class would be useless if we don't listen to its ThisHappened action.

My point is, the class I'm writing requires method calls. But it will compile just fine if you don't call those methods and will end up with an empty new FooManager. At some point, it will either not work, or maybe crash, depending on the class and what it does. The programmer that implements my class would obviously look inside it and realize "Oh, I didn't call Initialize!", and it'd be fine.

But I don't like that. What I would ideally want is the code to NOT compile if the method wasn't called; I'm guessing that's simply not possible. Or something that would immediately be visible and clear.

I find myself troubled by the current approach I have here, which is the following:

Add a private Boolean value in the class, and check everywhere necessary if the class is initialized ; if not, I will throw an exception saying "The class wasn't initialized, are you sure you're calling .Initialize(string,string)?".

I'm kind of okay with that approach, but it leads to a lot of code that is compiled and, in the end, not necessary to the end user.

Also, it's sometimes even more code when there are more methods than just an Initiliaze to call. I try to keep my classes with not too many public methods/actions, but that's not solving the problem, just keeping it reasonable.

What I'm looking for here is:

  • Is my approach correct?
  • Is there a better one?
  • What do you guys do/advise?
  • Am I trying to solve a non-issue? I've been told by colleagues it's the programmer's to check the class before trying to use it. I respectfully disagree, but that's another matter I believe.

Put it simply, I'm trying to figure out a way to never forget to implement calls when that class is reused later, or by someone else.

CLARIFICATIONS :

To clarify many questions here :

  • I'm definitely NOT only talking about the Initialisation part of a class, but rather it's whole lifetime. Prevent colleagues to call a method twice, making sure they call X before Y, etc. Anything that would end up being mandatory and in documentation, but that I would like in code and as simple and small as possible. I really liked the idea of Asserts, though I'm quite sure I'll need to mix some other ideas as Asserts will not always be possible.

  • I'm using the C# language ! How did I not mention that?! I'm in a Xamarin environment and building mobile apps usually using about 6 to 9 projects in a solution, including PCL's, iOS, Android and Windows projects. I've been a developer for about a year and a half (school and work combined), hence my sometimes ridiculous statements\questions. All that is probably irrelevant here, but too much information isn't always a bad thing.

  • I can't always put everything that is mandatory in the constructor, due to platform restrictions and the use of Dependency Injection, having parameters other than Interfaces is off the table. Or maybe my knowledge is not sufficient, which is highly possible.
    Most of the time it's not an Initialisation issue, but more

how can I make sure he registered to that event ?

how can I make sure he didn't forget to "stop the process at some point"

Here I remember an Ad fetching class. As long as the view where the Ad is visible is visible, the class would fetch a new ad every minute. That class needs a view when constructed where it can display the Ad, that could go in a parameter obviously. But once the view is gone, StopFetching() must be called. Otherwise the class would keep fetching ads for a view that isn't even there, and that's bad.

Also, that class has events that must bé listened to, like "AdClicked" for example. Everything works fine if not listened to, but we lose tracking of analytics there if taps aren't registered. The Ad still works though, so the user and developer won't see a difference, and analytics will just have wrong data. That needs to be, avoided, but I'm not sure how developer can know they must register to the tao event. That is a simplified example though, but the idea is there, "make sure he uses the public Action that is available" and at the right times of course!

Best Answer

In such cases, it is best to use the type system of your language to help you with proper initialization. How can we prevent a FooManager from being used without being initialized? By preventing a FooManager from being created without the necessary information to properly initialize it. In particular, all initialization is the responsibility of the constructor. You should never let your constructor create an object in an illegal state.

But callers need to construct a FooManager before they can initialize it, e.g. because the FooManager is passed around as a dependency.

Don't create a FooManager if you don't have one. What you can do instead is pass an object around that lets you retrieve a fully constructed FooManager, but only with the initialization information. (In functional-programming speak, I'm suggesting you use partial application for the constructor.) E.g.:

ctorArgs = ...;
getFooManager = (initInfo) -> new FooManager(ctorArgs, initInfo);
...
getFooManager(myInitInfo).fooMethod();

The problem with this is that you have to supply the init info every time you access the FooManager.

If it's necessary in your language, you can wrap the getFooManager() operation in a factory-like or builder-like class.

I really want to do runtime checks that the initialize() method was called, rather than using a type-system-level solution.

It is possible to find a compromise. We create a wrapper class MaybeInitializedFooManager that has a get() method that returns the FooManager, but throws if the FooManager wasn't fully initialized. This only works if the initialization is done through the wrapper, or if there is a FooManager#isInitialized() method.

class MaybeInitializedFooManager {
  private final FooManager fooManager;

  public MaybeInitializedFooManager(CtorArgs ctorArgs) {
    fooManager = new FooManager(ctorArgs);
  }

  public FooManager initialize(InitArgs initArgs) {
    fooManager.initialize(initArgs);
    return fooManager;
  }

  public FooManager get() {
    if (fooManager.isInitialized()) return fooManager;
    throw ...;
  }
}

I don't want to change the API of my class.

In that case, you'll want to avoid the if (!initialized) throw; conditionals in each and every method. Fortunately, there is a simple pattern to solve this.

The object you provide to users is just an empty shell that delegates all calls to an implementation object. By default, the implementation object throws an error for each method that it wasn't initialized. However, the initialize() method replaces the implementation object with a fully-constructed object.

class FooManager {
  private CtorArgs ctorArgs;
  private Impl impl;

  public FooManager(CtorArgs ctorArgs) {
    this.ctorArgs = ctorArgs;
    this.impl = new UninitializedImpl();
  }

  public void initialize(InitArgs initArgs) {
    impl = new MainImpl(ctorArgs, initArgs);
  }

  public X foo() { return impl.foo(); }
  public Y bar() { return impl.bar(); }
}

interface Impl {
  X foo();
  Y bar();
}

class UninitializedImpl implements Impl {
  public X foo() { throw ...; }
  public Y bar() { throw ...; }
}

class MainImpl implements Impl {
  public MainImpl(CtorArgs c, InitArgs i);
  public X foo() { ... }
  public Y bar() { ... }
}

This extracts the main behaviour of the class into the MainImpl.

Related Topic