3

If you have a class representing your applicative config file.

Instead of injecting that config class everywhere, would it be good application of interface segregation principle to expose several interfaces (based on config sections ?) on the class and inject them one or several of those to the consumers of that configuration (real implementation having only one instance).

I was thinking it would help quickly read which actual bits of configuration matters for a specific module, and make things clearer by only exposing relevant bits of config to each module.

Is it overthinking ISP ? Does ISP apply to data class ?

1
  • Yes, sounds like a fine idea. If the config file format is hierarchical you can use the structure itself to segregate unrelated settings.
    – JacquesB
    Apr 16 at 15:44

4 Answers 4

5

Yes ISP applies. At least the idea behind it does. Oh sure ISP was supposed to be about interfaces but data structures need organizing as well. It’s nice when your data structure has just what you need and nothing you don’t.

Yes you can push that too far. But we should acknowledge that dumping everything into one thing comes with a cost.

2
  • "ISP was supposed to be about interfaces but data structures need organizing as well" - that sounds as if data structures have no interfaces?
    – Bergi
    Apr 16 at 23:11
  • @Bergi well strictly speaking interfaces ARE data structures. But you don’t need abstraction, encapsulation, or data hiding to appreciate your data structure being free of clutter. Apr 17 at 1:39
0

Yes, but...

When applied dogmatically you can end up arguing that every config value has its own individual purpose, which is not incorrect by itself, but it would be incorrect to then assume that therefore each config value needs its own interface.

If you do this, what's going to end up happening is that at some point, someone is going to bundle these individual settings to make it easier to pass them, and at that point all you've done is create a bunch of boilerplate.

Break up your config into sections that make sense. Definitely don't keep it as a monolith, but don't go down to the molecular level either.

0

The Configuration object should not be pushed everywhere

This question has an implied premise:

If you have a class representing your applicative config file [and everything that consumes the config file depends on that object].

I believe this premise is wrong. Even if you were to apply ISP and layer a bunch of interfaces on top of the config object, that still means that there is a single config object which needs to know the config schema for every part of your application. Any change which can be configured now requires editing that object.

Only one module should be responsible for reading the config

A complicated application will likely have multiple sources of configuration. For example, there might be a big config file, but also a few command line overrides, or even a smaller overrides file, plus some environment variables, and maybe some remote settings pulled from an update server somewhere.

Wrangling all of these data sources is the responsibility of a Config/Loader module.

Other modules only responsibility toward configuration is to be configured

The config/loader module doesn't need to care about the content that is being configured. Different components can then be free to define their own configurable properties, and the loader/configurator is responsible for populating those properties with the configured values.

This can be handled by having a per-module POD FooConfig object, but I personally prefer to have configuration be an attribute of various properties - rather than FooRetryAttemptBackoffDuration being a property of some config object, RetryAttemptBackoffDuration is a property of the Foo object directly (which also contains the method which consumes that property).

The config file schema can then be something like JSON or XML, and the Foo object is deserialized into existence from the config file by the loader (after it applies command line overrides or pulls from whatever additional data sources contribute to the config).

4
  • 1
    I like this answer, but it too has it's own premise which might not be correct: that every object requiring configuration data can be deserialized from XML or JSON (or some similar format). This isn't wrong in all scenarios, nor is it right in all scenarios. For cases where the object can be deserialized from a config file, this answer is spot on, IMO. Apr 16 at 19:36
  • Another aspect this answer hints at is removing configuration as a dependency. This is difficult to do unless you can use a factory or deserialize the object. Apr 16 at 19:41
  • 2
    "Config objects should be responsible for reading the config" - I don't think so. The loader should be separated from the POD objects that represent the configuration. Sure, sometimes the config loader can act as a factory or DI provider and directly construct the objects that need to be configured, but sometimes it can't. And even when it does construct the target objects, the interface segregation principle applies and says the loader should only need to know the construction interface of these objects.
    – Bergi
    Apr 16 at 23:23
  • 1
    @Bergi - I'll re-word that section. What I meant to say is that there is only one module that is responsible for handling the "configuration" (whether you call that module a configuration module or a loader module). The other components only responsibility with regards to configuration is to be configured.
    – Tim C
    Apr 17 at 16:44
0

If by "data class" you mean a class whose fields are all public (think: Data Transfer Object) then I don't think the Interface Segregation Principal applies. From Wikipedia, the ISP states that "no code should be forced to depend on methods it does not use." The keyword here is methods — behavior. No code should depend on behavior it doesn't need.

If we take this strict approach and consider a purely data class, then there is no behavior to segregate. The principal doesn't make sense in this context.

Further along, Wikipedia states that the "ISP is intended to keep a system decoupled and thus easier to refactor, change, and redeploy." The intention of the ISP is to make changing the system easier by limiting the blast radius of those changes. The Interface Segregation Principal is meant as a thought experiment for the consumers of an object: "What is the bare minimum data or behavior required to do my job?" The less you know about the wider system, the less likely your code will need to be changed in the future as a result of refactoring or reorganizing the code base. If we take this more abstract approach, then the ISP can make sense for configuration or purely data objects. Data structures can change over time in response to the changing needs of the application. Configuration is no different.

Most programming languages do not allow you to define a field as a member of an interface, but don't get too hung up on the code construct. Interface can be a more general term describing the members of an object that the outside world is allowed to use, be it data or behavior.

So, the Interface Segregation Principal isn't strictly reduced to consuming public interface IFoo types of objects. Polymorphism is not the driving force behind the ISP.

In reference to purely data classes, you would define a data class which only contains the minimal fields necessary paired with a clear name indicating why these data are related. You don't have one config object implementing multiple interfaces. Instead you have multiple classes defining the minimum data members required for that configuration. Composition becomes the more relevant tool here, rather than inheritance. Decompose configuration into smaller classes. As others have mentioned, you could end up with a deeper structure of objects rather than one large monolithic config object. Since each config object has fewer data fields exposed publicly, you adhere to the Interface Segregation Principal just the same. The ISP is applied according to the members available to consuming code — the more abstract definition of interface instead of the programming language keyword interface.

2
  • I went back and forth in my head before posting this answer, because it basically rehashes bits and pieces from the other answers, but I thought all of those ideas needed to be assembled into a cohesive post to fully address what I think is driving this question: confusion about what "interface" means in the ISP. Apr 20 at 0:40
  • "a class whose fields are all public" - it could also refer to a class with private fields and public getters for them. I think the ISP really is meant for the abstract definition of what makes a type - and languages where interfaces can declare members acknowledge that.
    – Bergi
    Apr 20 at 3:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.