Question

I am trying to look for the right design pattern for the below scenario.

I am trying to create an object/binary with different modules, a module is a functionality that I want to provide to that object. Examples of a module are like reading a file, writing a file, sending some requests to the server, etc...

Let say I have the following functions

func readFiles(path) {
  /* */ 
}

func writeFiles(Content, Path) {
  /* */
}

func playMusic(path) {
    /*  */ 
}

func playVideo(path) {
    /* */
} 

The object/client program will usually have the following feature by taking input from the user

switch input {
    case "playmusic":
       call playmusic('path') 
    case "playvideo":
       call playvideo('path') 
    case "readfile":
       call readfile('path') 
    case "writefile":
       call writefile('path') 
    default:
       /* Cannot understand your input */ 
}

The above approach works fine if I want to include all the functions in the object, and I will generate a binary that contains all the functions.

But for now, I want to provide a greater granularity when generating the binary. I want the user to be able to choose the function they want to include in the binary and hence the unused functions will not be included in the binary. What design pattern will be suitable for that?

I have thought about decorator pattern, but that is not exactly right since I'm not decorating a function, they are all separate functions.

I have checked the strategy pattern, but that is not exactly right as well since it will still include all the functions but just changing the strategy at runtime.

Was it helpful?

Solution

"Include in the binary" means that you use a compiled language. Design patterns alone are not sufficient to obtain this effect.

3 scenarios can achieve this, if your language supports it:

  • conditional compilation: the preprocessor excludes some parts of the code from the compilation process. Some languages achieve the same result without preprocessor by identifying constant expressions and eliminating unreached code.
  • meta programming: the language construct generate some code at compilation depending on compile time parameters (e.g. C++ templates). Policy based design is an example where patterns combined with meta programming can do exactly what you are looking for.
  • intelligent linking: the linker only includes the modules that are needed. This is how libraries work: the independent modules are packaged into a library; the linker will include in the binary only the modules of which symbols (e.g. playmusic) that appear in the object code to link (i.e the code compiled by your client)

Of course, all these means require your different parts to be encapsulated and decoupled. Here patterns could help.

If your language doesn't support these possibities, then all your code might be included in the output (bytecode, compressed source code, ...). All you could do is then to use some feature toggles and use some configuration code to activate the toggle and prevent some parts of being accidentally invoked.

Licensed under: CC-BY-SA with attribution
scroll top