Aiming for object-oriented design elegance
Tags: oop, java, objectorientation, solid
Published: 2022-03-16 16:23:55

---
title: Aiming for object-oriented design elegance
published: true
date: 2022-03-15 00:00:00 UTC
tags: OOP,Java,objectorientation,solid
canonical_url: https://MihaiBojin.com/application-settings/oop-design
---

🔔 This article was originally posted on my site, [MihaiBojin.com](https://MihaiBojin.com/application-settings/oop-design?utm_source=DevTo&utm_medium=organic&utm_campaign=top-promo "MihaiBojin.com"). 🔔

* * *

One of my goals for this project was to create a library that is elegant and easy to use while feeling Java idiomatic.

Let's talk [SOLID](https://en.wikipedia.org/wiki/SOLID). They're a set of five principles aimed at making object-oriented design implementations flexible and maintainable. I am designing a library that I hope will be a joy to use and will make developers want to adopt it, for which reason I interpreted these principles in the best possible form I could think of.

My initial thought was to offer a base `Prop` interface, abstracting away lower-level implementation details that are not relevant to users of this class.

However, I settled on using a few abstract classes, for several reasons, inspired by a few of the SOLID principles:

- all `Prop` objects need a few common traits
- a class should have a single responsibility; since I was building multiple themes, sticking them all into a single class didn't feel elegant
- it should be easy to build on top of each layer
- not all methods need to be exposed in the final public contract; unfortunately, Java interfaces do not support non-public methods

Let's break it down; here is the high-level end-result class design:

```
@FunctionalInterface
public interface Subscribable<T> {
  void subscribe(Consumer<T> onUpdate, Consumer<Throwable> onError);
}

public abstract class SubscribableProp<T> implements Subscribable<T> {
  /* Processes a value update event. */
  protected void onValueUpdate(@Nullable T value, long epoch) {}
  /* Processes an exception encountered during an update. */
  protected void onUpdateError(Throwable error, long epoch) {}
}

public abstract class Prop<T> extends SubscribableProp<T> implements Supplier<T> {
    /* Identifies the Prop */
    public abstract String key();

    /* Returns the Prop's value */
    public abstract T get();
}

public abstract class BoundableProp<T> extends Prop<T> {
  /* Allows the Registry to update a Prop's value */
  protected abstract boolean setValue(@Nullable String value);
}

public class Registry {
  /* Binds a Prop object to the Registry object, allowing it to process update events and set the Prop's value */
  public <T, PropT extends BoundableProp<T>> PropT bind(PropT prop) {}
}
```

`Subscribable` denotes that a `Prop` can be subscribed to. The result of a prop update is either success or an error.

`SubscribableProp` is a partial implementation that hosts the logic necessary to process updates/errors and notify clients safely.

`Prop` is the absolute minimum public contract that a consumer/client should care about. It defines an identifier (`key`) and a way to get the prop's `value`.

Finally, `BoundableProp` encompasses all of the above and also includes a mechanism that allows the `Registry` to update prop values when the underlying sources are updated.

However, in practice, relying on a key and value alone, is not enough of a reason to adopt this library.

For that reason, the `CustomProp` class provides an almost complete implementation, par the corresponding `Converter.decode()` method, which requires a knowledge of the Prop's type.

```
public abstract class CustomProp<T> extends BoundableProp<T> implements Converter<T> {
    /* Identifies the Prop */
    public String key() {};
    /* Returns the Prop's value */
    public String get() {};
    /* Describes the prop */
    public String description() {};
    /* true, if the prop is required */
    public boolean isRequired() {};
    /* true, if the prop is a secret */
    public boolean isSecret() {};
}

@FunctionalInterface
public interface Converter<T> {
  /* Decodes a String into the desired type; must be implemented */
  T decode(@Nullable String value);

  /* Encodes the value into a String, defaulting to using Object.toString() */
  default String encode(@Nullable T value) {
    return value == null ? null : value.toString();
  }
}
```

One way to extend `CustomProp` is to provide an implementation for `Converter.decode`, thus completing the class, e.g.:

```
public class LongProp extends CustomProp<Long> {
  public Long decode(String value) {
    Number number = safeParseNumber(value);
    try {
      return NumberFormat.getInstance().parse(value).longValue();
    } catch (ParseException e) {
      log.log(SEVERE, e);
      return null;
    }
  }    
}
```

However, we can do a bit better. Since one can assume that most props will be of common Java datatypes, I have provided a series of default converters that can be composed into a final implementation. The above code can be rewritten as follows:

```
public class LongProp extends CustomProp<Long> implements LongConverter {  
}

public interface LongConverter extends Converter<Long> {
   @Override
   public Long decode(String value) {
    Number number = safeParseNumber(value);
    try {
      return NumberFormat.getInstance().parse(value).longValue();
    } catch (ParseException e) {
      log.log(SEVERE, e);
      return null;
    }
  }  
}
```

How would we use this in production? Here's a small complete excerpt:

```
    Source source = new PropertyFile(PATH_TO_PROP_FILE);
    Registry registry = new RegistryBuilder(source).build();

    Prop prop = registry.bind(new LongProp("a.key"));

    prop.get(); // will return the value corresponding to a.key
    prop.subscribe(updatedValue -> {/* process updates */}, 
                   error -> {/* process any errors */});
```

* * *

Hopefully, this article serves as a good high-level introduction to the contract one can expect from the [props](http://github.com/props-sh/props) library.

In future series I'd like to explore the props library's API a bit more and show a few real-world examples of how it could be used to simplify application settings/property management in Java projects.

As always, any feedback is welcome; feel free to ping me on [Twitter](https://twitter.com/mihaibojin).

Thanks!

* * *

If you liked this article and want to read more like it, [please subscribe to my newsletter](https://motivated-founder-807.ck.page/db1cf284bf "newsletter link"); I send one out every few weeks!