0

I am developing a package for the Unity game engine. I want to add additional functionality to my package if another specific package is present. I am faced with a dilemma I am not sure which of these two options is better in terms of API design and usability.

While not frequent, one can expect users to swap between having the other package installed or not. It would also be a fair assumption to say yet another third package might use my package as a dependency.

Thank you in advance for your thoughts and suggestions.

1. Stub values and exceptions

This option would mean the public API of my package won't change based on whether or not the user has the other package installed, but the values they would get would either be stubs or throw exceptions. Example:

public bool FooBar => 
#if OTHER_LIB_PRESENT
 OtherLib.Value();
#else
 false; // or throw an exception
#endif

2. Public API change

This option means the public API of my package would change depending on whether or not the user has the other package installed. Example:

#if OTHER_LIB_PRESENT
public bool FooBar => OtherLib.Value();
#endif
asked May 9, 2023 at 16:44
4
  • 2
    Both options sound like violating the Principle of least astonishment. Why not put this this decision into the hands of the users who are installing the package instead of trying to force the decision for them? (i.e. if they have both packages installed, then simply give them a way to switch/choose in the code itself). Perhaps as a configuration parameter or Startup option, to avoid forcing a 'silent' override; otherwise such silent alterations like this can cause developers to rip out their hair out in frustration. Commented May 9, 2023 at 17:18
  • @BenCottrell I actually don't quite understand the option you are suggesting. Would you mind elaborating a bit more? Commented May 9, 2023 at 17:27
  • @BenCottrell I believe I kind of understand it. So, in the tech stack I am working with, I can develop an additional package, which, when installed, would enable the conditional features. And users can choose to have it or not. But the problem I have with that is, if the other library (the other_lib condition) is present then it is almost a guarantee that the conditional features will be desired by the user, because they are very much interconnected. Commented May 9, 2023 at 17:34
  • 2
    @starikcetin no, not an additional package. Make it a configuration option that the developer can set. For example, if it's set it uses the OtherLib.Value(); if it's not it doesn't. And if it's set but they don't have OtherLib installed, an exception naturally gets thrown (ModuleNotFoundException or something like that) Commented May 9, 2023 at 18:57

1 Answer 1

2

Neither option is good. Conditional compilation requires two sets of binaries, and just complicates the build process. Instead, declare an interface in your project that has the methods or properties your project needs, and give it a meaningful name:

public interface ISomethingMeaningful
{
 bool Value();
}

Utilize the null object pattern to provide a default implemendation:

public DefaultSomethingMeaningful : ISomethingMeaningful
{
 public bool Value() => false;
}

Create a separate project providing an additional implementation of the interface for the other library:

public class OtherLibSomethingMeaningful : ISomethingMeaningful
{
 public bool Value()
 {
 return // something that makes sense with this other library
 }
}

Classes that need this functionality should require an ISomethingMeaningful object in its constructor. You can provide the default implementation in constructors that do not declare an ISomethingMeaningful parameter:

public class Foo
{
 private ISomethingMeaningful Something { get; }
 // No null-checks or conditional compilation required
 public bool FooBar => Something.Value();
 public Foo() : this(new DefaultSomethingMeaningful())
 {
 }
 public Foo(ISomethingMeaningful something)
 {
 Something = something;
 }
}

This requires two projects, but gives other applications some flexibility, which is desirable in this case.

answered May 9, 2023 at 19:49

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.