Home .NET Dependencyimplementation patterns.Part 1

Dependencyimplementation patterns.Part 1

by admin

Let’s discover how to implement dependencies in .Net, since this topic is one of the must-reads for writing good, flexible and testable code.We’ll start with the most basic and necessary patterns of implementing dependencies:implementing via constructor and via property.So, let’s go!

Implementing through the constructor

Destination

Break the rigid link between a class and its mandatory dependencies.

Description

The essence of the pattern is that all the dependencies required by some class are passed to it as constructor parameters represented as interfaces or abstract classes
How can you ensure that the required dependency is always available to the class being developed?
This is ensured if all calling classes will pass the dependency as a constructor parameter.
A class that requires a dependency must have a constructor with an access modifier public (public), which gets an instance of the required dependency as a constructor argument :

private readonly IFoo _foo;publicFoo(IFoo foo){if (foo == null)throw new ArgumentNullException(nameof(foo));_foo = foo;}

Dependence is mandatory constructor argument. Any client code that does not provide a dependency instance cannot compile. However, since as interface , and abstract class are referenced types, the calling code can pass a special value of null , which makes the application compilable.Therefore, the class makes a check for null , which protects the class from such incorrect use.Since the compiler and the protection unit work together (check for null ) ensures that the constructor argument is correct (unless an exceptional situation ( Exception )), the constructor can just save the dependency for future use, without figuring out the details of the actual implementation.
It is good practice to declare the field storing the dependency value as " read only " ( Read-Only ). In this way we ensure that the execution, and only once , the initialization logic in the constructor : field cannot be modified This is not necessary to implement dependency implementation, but it protects the code from accidental modifications of the field (e.g., from setting its value to null ) somewhere else in the class code.

When and how implementation via constructor should be used

Implementing through the constructor should be used by default with dependency implementation. It implements the most popular scenario when a class needs one or more dependencies and no suitable local defaults are available.
Consider the best tips and practices for using implementation through the constructor :

  • If possible, you should limit the class to one constructor
  • Overloaded constructors provoke ambiguities: which constructor should dependency implementation use?
  • Do not add any other logic to the constructor
  • Dependencydoesn’t need to be checked for nullanywhere else in the class, because the constructor guarantees it

Dignities Disadvantages
Implementation guaranteed In some frameworks it is difficult to implement through constructor
Simplicity of realization Require immediate initialization of entire dependency graph (*)
Providing a clear contract between a class and its clients (it’s easier to think about the current class without thinking about where the higher-level class’s dependencies come from)
The complexity of the class becomes apparent

(*)The obvious disadvantage of implementing the constructor is the requirement immediate initialization of the entire dependency graph – often already at application startup. Nevertheless, while this drawback seems to reduce the efficiency of the system, it is rarely a problem in practice. Even for complex object graphs, creating an object instance is an action that NET framework performs extremely quickly. In very rare cases, this can be a really serious problem. Then let’s use a lifecycle parameter called Delayed (delayed), which is quite suitable for solving this problem.
A potential problem with using a constructor to pass dependencies could be that the constructor parameters increase too much. Here you can read more about it here.
Another reason for the large number of constructor parameters could be that too many abstractions This state of affairs may indicate that we have begun to abstract even from things from which we need not abstract at all : we have started to make interfaces for objects that just store data, or classes whose behavior is stable, independent of their external environment, and which should be explicitly hidden inside the class rather than exposed outside it.

Examples of usage

Implementing through the constructor ( Constructor Injection ) is a basic dependency injection pattern and it is intensively used by most programmers, even if they don’t think about it. One of the main goals of most "standard" design patterns (GoF patterns) is to get loosely coupled design, so it’s not surprising that most of them use some form of dependency implementation.
So, decorator uses a dependency implementation via the constructor; strategy is passed through a constructor, or is "embedded" in the desired method; command can be passed as a parameter, or it can be received via the constructor surrounding context Abstract Factory is often passed through a constructor and is by definition implemented through an interface or abstract class; pattern State takes the necessary context as a dependency, etc.
Two examples demonstrating the use of constructor implementation in BCL , use classes System.IO.StreamReader and System.IO.StreamWriter
They both get an instance of the class System.IO.Stream to the constructor.

public StreamWriter(Streamstream);public StreamReader(Streamstream);

Class Stream – is an abstract class that acts as the abstraction by which the StreamWriter and StreamReader You can pass any implementation of the class Stream to their constructors, and they will use it. But if you try to pass in a constructor as Stream value null will be generated by ArgumentNullExceptions

// Decoratorsvar ms = new MemoryStream();var bs = new BufferedStream(ms);// Sorting Strategyvar sortedArray = new SortedList<int, string> (new CustomComparer());// The ResourceReader class accepts StreamStream ms = new MemoryStream();var resourceReader = new ResourceReader(ms);// BinaryReader/BinaryWriter, StreamReader/StreamWriter// also accept Stream via constructorvar textReader = new StreamReader(ms);// Icon accepts Streamvar icon = new System.Drawing.Icon(ms);

Output
Regardless of whether you use DI containers or not, implementation via the constructor ( Constructor Injection ) should be the first way to manage dependencies. Its use will not only make the relationships between classes more explicit, but will also allow you to identify design problems when the number of constructor parameters exceeds a certain limit. In addition, all modern dependency implementation containers support this pattern.

Property Injection

Destination

Break the rigid link between a class and its optional dependencies.

Description

How can I allow implementing dependencies as an option in a class if there is a suitable local default?
Using a writeable property, which allows the caller to set its value if they want to replace the default behavior.
A class that uses a dependency must have a writeable property with the modifier public : the type of this property must match the type of the dependency.

public class SomeClass{public ISomeInterfaceDependency{ get; set; }}

Here SomeClass depends on ISomeInterface Clients can pass implementations of the interface ISomeInterface via the property Dependency Note that, as opposed to introducing a constructor, you not can check the property field Dependency as " read only " ( Read Only ) because the caller is allowed to change the value of this property in any point in the lifecycle of the class SomeClass
Other members of a dependent class can use the injected dependency to perform their functions, such as :

public string DoSomething(string message){return this.Dependency.DoStuff(message);}

However, this implementation is unreliable since the property Dependency does not guarantee that an instance of ISomeInterface For example, the code below will generate NullReferenceException , because the value of the property Dependency is null :

var sc = new SomeClass();sc.DoSomething("Hello world!");

This problem can be eliminated by setting the default dependency instance constructor for the property, combined with adding a check for null to the method – property installer.

public class SomeClass{private ISomeInterface _dependency;public SomeClass(){_dependency = new DefaultSomeInterface();}publicISomeInterface Dependency{get => _dependency;set => _dependency = value ?? throw new ArgumentNullException(nameof(value));}}

The difficulty arises if customers will be allowed to change dependency value during the class life cycle.
What should happen if the client tries to change the dependency value during the class lifecycle?
The consequence of this can be inconsistent or unexpected behavior of the class, so it is better to guard against this turn of events.

public class SomeClass{private ISomeInterface _dependency;public ISomeInterface Dependency{get => _dependency ?? (_dependency = new DefaultDependency());set{//It is allowed only 1 time to define the dependencyif (_dependency != null)throw new InvalidOperationException(nameof(value));_dependency = value ?? throw new ArgumentNullException(nameof(value));}}}

Creating DefaultDependency can be delayed until the property is requested for the first time.In that case, a delayed initialization will occur. Note that the local default is assigned through setter with the modifier public which ensures that all the protection blocks are fulfilled. The first protection block ensures that the dependency being set is not null (we can catch NRE ).The next guard block is responsible for making sure that the dependency is only set to one times.
You may also notice that, the dependence will blocked after the property has been read. This is done to protect clients from situations where the dependency later changes without any notification, while the client thinks the dependency remains the same.

When to apply the implementation of the properties

Implementing a property should only be used when there is a suitable local default but you would like to leave the calling party free to use a different implementation of the dependency type. Implementing the property is best applied if the dependency is optional It should be assumed that the properties are optional because it’s easy to forget to assign a value to them, and the compiler will not react at all.
It may seem tempting to set this default implementation for a given class at design time. However, if such an advance default is implemented in another assembly ( Assembly ), using it in this way will inevitably cause the creation of immutable reference to it, which would negate many of the advantages of weak binding

Cautions

  • Using Property Injectionfor mandatory dependencies. This is one of the most common mistakes in using this pattern. If a class necessarily needs some dependency, it should be passed through the constructor so that it is in a valid state immediately after the object is created.
  • Using Foreign Defaultinstead of Local Default. One of the dangers of using a default dependency implementation is using a specific dependency located in an assembly that our service should not know about ( Foreign Default ).If there are many such services, we will get dozens of extra physical links, which will complicate the understanding and maintenance. The default implementation should be in the same assembly( Local Default ).
  • Difficulty. The problem of using Property Injection for mandatory dependencies is that it greatly increases the complexity of the class. A class with three fields, each of which can be null, results in 8 different combinations of object states. Trying to check the state in the body of each open method leads to an unnecessary jump in complexity.
  • Binding to a container. In most cases, we must use a container in minimal number of spaces. Using Constructor Injection generally achieves this, since its use does not bind your class to any particular container. However, the situation changes when you use Property Injection Most containers contain a set of specialized attributes for managing dependencies via properties ( SetterAttribute for StructureMap , Dependency for Unity , DoNotWire for Castle Windsor etc.). This rigid connection will prevent you from "changing your mind" and switching to another container or abandoning them altogether.
  • Write-only properties. It’s not always the case that we want to expose a property that returns a dependency. In this case, we either have to make the property write-only ( set-only property ), which is contrary to generally accepted design principles on the NET Or to use a method instead of a property ( Setter Method Injection ).
    public class SomeClass{private ISomeInterface _dependency;public void SetDependency(ISomeInterface dependency){_dependency = dependency;}}

Alternatives

If we have a class that contains optional dependency, we can use the old approach with two constructors :

public class SomeClass{private ISomeInterface _dependency;public SomeClass() : this(new DefaultSomeInterface()){ }public SomeClass(ISomeInterface dependency){_dependency = dependency;}}

Conclusion

Implementation via Property ( Property Injection ) is ideal for optional dependencies. They are fine for strategies with default implementations, but I would still recommend using Constructor Injection. and would only consider other options if needed.

You may also like