How to use dependency injection and avoid temporal coupling?Are init() methods a code smell?How does dependecy injection increase coupling?Reducing dependency cycles and reducing couplingHow can I avoid tight coupling when practically every decision-logic has to check lots of distributed state?Qualified dependency injection and couplingHow to prevent dependency injection from killing object oriented programmingDependency injection for a library with internal dependenciesHandling disposables with dependency injectionHow best to avoid member implementation of class' Abstract/Interface instance variables
Would a physician with migraine better treat their patients?
Is the value of a probability density function for a given input a point, a range, or both?
Making part of lines layer transparent based on circle
What is the word for things that work even when they aren't working (e.g. escalators)?
Why is it popular to teach modulus via the example of mod 12 and analogue clocks?
Why are seats at the rear of a plane sometimes unavailable even though many other seats are available in the plane?
What does the British parliament hope to achieve by requesting a third Brexit extension?
Does my code handle negative numbers or zero when summing squared digits?
What's the current zodiac?
Rationalism and Catholicism / Protestantism
Giving a character trauma but not "diagnosing" her?
Appending to each string in a list with mapping
Equation with indices at end and to right side
Pass a bash variable to python script
What is the German word for: "It only works when I try to show you how it does not work"?
Can a character dodge an attack that beats their Armor Class?
d-Menthol vs dl-menthol: Does an enantiomer and its racemic mixture have different melting points?
How safe is using non-RoHS parts?
What is the purpose of the redundant "いい人" in this example sentence
Legality of creating a SE replica using SE's content
How can a "proper" function have a vertical slope?
'provocative' vs 'sexy'
Why does this route work with a slash and not a dash?
Dynamics m, r, s, and z. What do they mean?
How to use dependency injection and avoid temporal coupling?
Are init() methods a code smell?How does dependecy injection increase coupling?Reducing dependency cycles and reducing couplingHow can I avoid tight coupling when practically every decision-logic has to check lots of distributed state?Qualified dependency injection and couplingHow to prevent dependency injection from killing object oriented programmingDependency injection for a library with internal dependenciesHandling disposables with dependency injectionHow best to avoid member implementation of class' Abstract/Interface instance variables
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
Suppose I have the Service that receives dependencies via constructor but also needs to be initialized with custom data (context) before it can be used:
public interface IService
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
public void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
public class Context
public int Value1;
public string Value2;
public string Value3;
Now - the context data is not know beforehand so I cannot register it as a dependency and use DI to inject it into the service
This is how example client looks like:
public class Client
private readonly IService service;
public Client(IService service)
this.service = service ?? throw new ArgumentNullException(nameof(service));
public void OnStartup()
service.Initialize(new Context
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
);
public void Execute()
service.DoSomething();
service.DoOtherThing();
As you can see - there are temporal coupling and initialize method code smells involved, because I first need to call service.Initialize to be able to call service.DoSomething and service.DoOtherThing afterwards.
What are the other approaches in which I can eliminate these problems?
Additional clarification of the behavior:
Each instance of the client needs to have it's own instance of the service initialized with client's specific context data. So, that context data is not static or known in advance so it cannot be injected by DI in the constructor.
dependency-injection coupling
add a comment
|
Suppose I have the Service that receives dependencies via constructor but also needs to be initialized with custom data (context) before it can be used:
public interface IService
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
public void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
public class Context
public int Value1;
public string Value2;
public string Value3;
Now - the context data is not know beforehand so I cannot register it as a dependency and use DI to inject it into the service
This is how example client looks like:
public class Client
private readonly IService service;
public Client(IService service)
this.service = service ?? throw new ArgumentNullException(nameof(service));
public void OnStartup()
service.Initialize(new Context
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
);
public void Execute()
service.DoSomething();
service.DoOtherThing();
As you can see - there are temporal coupling and initialize method code smells involved, because I first need to call service.Initialize to be able to call service.DoSomething and service.DoOtherThing afterwards.
What are the other approaches in which I can eliminate these problems?
Additional clarification of the behavior:
Each instance of the client needs to have it's own instance of the service initialized with client's specific context data. So, that context data is not static or known in advance so it cannot be injected by DI in the constructor.
dependency-injection coupling
add a comment
|
Suppose I have the Service that receives dependencies via constructor but also needs to be initialized with custom data (context) before it can be used:
public interface IService
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
public void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
public class Context
public int Value1;
public string Value2;
public string Value3;
Now - the context data is not know beforehand so I cannot register it as a dependency and use DI to inject it into the service
This is how example client looks like:
public class Client
private readonly IService service;
public Client(IService service)
this.service = service ?? throw new ArgumentNullException(nameof(service));
public void OnStartup()
service.Initialize(new Context
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
);
public void Execute()
service.DoSomething();
service.DoOtherThing();
As you can see - there are temporal coupling and initialize method code smells involved, because I first need to call service.Initialize to be able to call service.DoSomething and service.DoOtherThing afterwards.
What are the other approaches in which I can eliminate these problems?
Additional clarification of the behavior:
Each instance of the client needs to have it's own instance of the service initialized with client's specific context data. So, that context data is not static or known in advance so it cannot be injected by DI in the constructor.
dependency-injection coupling
Suppose I have the Service that receives dependencies via constructor but also needs to be initialized with custom data (context) before it can be used:
public interface IService
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
public void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
public class Context
public int Value1;
public string Value2;
public string Value3;
Now - the context data is not know beforehand so I cannot register it as a dependency and use DI to inject it into the service
This is how example client looks like:
public class Client
private readonly IService service;
public Client(IService service)
this.service = service ?? throw new ArgumentNullException(nameof(service));
public void OnStartup()
service.Initialize(new Context
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
);
public void Execute()
service.DoSomething();
service.DoOtherThing();
As you can see - there are temporal coupling and initialize method code smells involved, because I first need to call service.Initialize to be able to call service.DoSomething and service.DoOtherThing afterwards.
What are the other approaches in which I can eliminate these problems?
Additional clarification of the behavior:
Each instance of the client needs to have it's own instance of the service initialized with client's specific context data. So, that context data is not static or known in advance so it cannot be injected by DI in the constructor.
dependency-injection coupling
dependency-injection coupling
edited May 2 at 12:50
Dusan
asked May 2 at 12:24
DusanDusan
3203 silver badges19 bronze badges
3203 silver badges19 bronze badges
add a comment
|
add a comment
|
5 Answers
5
active
oldest
votes
There are several ways to deal with the initialization problem:
- As answered in https://softwareengineering.stackexchange.com/a/334994/301401, init() methods are a code smell. Initializing an object is the responsibility of the constructor - that's why we have constructors after all.
- Add The given service must be initialized to the doc comment of
Clientconstructor and let the constructor throw if the service is not initialized. This moves the responsibility to the one who gives you theIServiceobject.
However, in your example, the Client is the only one that knows the values that are passed to Initialize(). If you want to keep it that way, I'd suggest the following:
- Add an
IServiceFactoryand pass it to theClientconstructor. Then you can callserviceFactory.createService(new Context(...))which gives you an initializedIServicethat can be used by your client.
The factories can be very simple and also allow you to avoid init() methods and use constructors instead:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
public Service createService(Context context)
return new Service(context);
In the client, OnStartup() is also an initialization method (it just uses a different name). So if possible (if you know the Context data), the factory should directly be called in the Client constructor. If that's not possible, you need to store the IServiceFactory and call it in OnStartup().
When Service has dependencies not provided by Client they would be provided by DI through ServiceFactory:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public ServiceFactory(object dependency1, object dependency2, object dependency3)
this.dependency1 = dependency1;
this.dependency2 = dependency2;
this.dependency3 = dependency3;
public Service createService(Context context)
return new Service(context, dependency1, dependency2, dependency3);
1
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
1
@Dusan don't use Service Locator. IfServicehas dependencies other than theContext, that would not be provided by theClient, they can be provided via DI to theServiceFactoryto be passed to theServicewhencreateServiceis called.
– Mr.Mindor
May 2 at 19:53
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);and be left with your partially set up service, then later doService s = partial.context(context).build()
– Aaron
May 2 at 23:00
add a comment
|
The Initialize method should be removed from the IService interface, as this is an implementation detail. Instead, define another class that takes the concrete instance of Service and calls the initialize method on it. Then this new class implements the IService interface:
public class ContextDependentService : IService
public ContextDependentService(Context context, Service service)
this.service = service;
service.Initialize(context);
// Methods in the IService interface
This keeps client code ignorant of the initialization procedure, except where the ContextDependentService class is initialized. You at least limit the parts of your application that need to know about this wonky initialization procedure.
add a comment
|
It seems to me that you have two options here
- Move the Initialisation code to the Context and inject an Initialised Context
eg.
public InitialisedContext Initialise()
- Have the first call to Execute call Initialise if its not allready done
eg.
public async Task Execute()
//lock context
//check context is not initialised
// init if required
//execute code...
- Just throw exceptions if Context isnt initialised when you call Execute. Like SqlConnection.
Injecting a factory is fine if you just want to avoid passing context as a parameter. Say only this particular implementation needs a context and you want not to add it to the Interface
But you essentially have the same problem, what if the factory hasn't got an initialised context yet.
add a comment
|
You should not depend your interface to any db context and initialize method. You can do it in concrete class constructor.
public interface IService
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
private readonly object context;
public Service(
object dependency1,
object dependency2,
object dependency3,
object context )
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
// context is concrete class details not interfaces.
this.context = context;
// call init here constructor.
this.Initialize(context);
protected void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
And, an answer of your main question would be Property Injection.
public class Service
public Service(Context context)
this.context = context;
private Dependency1 _dependency1;
public Dependency1 Dependency1
get
if (_dependency1 == null)
_dependency1 = Container.Resolve<Dependency1>();
return _dependency1;
//...
This way you can call all dependencies by Property Injection. But it could be huge number. If so, you can use Constructor Injection for them, but you can set your context by property by checking if it is null.
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
add a comment
|
Misko Hevery has a very helpful blog post about the case you've faced. You both need newable and injectable for your Service class and this blog post may help you.
add a comment
|
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "131"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f391290%2fhow-to-use-dependency-injection-and-avoid-temporal-coupling%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
There are several ways to deal with the initialization problem:
- As answered in https://softwareengineering.stackexchange.com/a/334994/301401, init() methods are a code smell. Initializing an object is the responsibility of the constructor - that's why we have constructors after all.
- Add The given service must be initialized to the doc comment of
Clientconstructor and let the constructor throw if the service is not initialized. This moves the responsibility to the one who gives you theIServiceobject.
However, in your example, the Client is the only one that knows the values that are passed to Initialize(). If you want to keep it that way, I'd suggest the following:
- Add an
IServiceFactoryand pass it to theClientconstructor. Then you can callserviceFactory.createService(new Context(...))which gives you an initializedIServicethat can be used by your client.
The factories can be very simple and also allow you to avoid init() methods and use constructors instead:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
public Service createService(Context context)
return new Service(context);
In the client, OnStartup() is also an initialization method (it just uses a different name). So if possible (if you know the Context data), the factory should directly be called in the Client constructor. If that's not possible, you need to store the IServiceFactory and call it in OnStartup().
When Service has dependencies not provided by Client they would be provided by DI through ServiceFactory:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public ServiceFactory(object dependency1, object dependency2, object dependency3)
this.dependency1 = dependency1;
this.dependency2 = dependency2;
this.dependency3 = dependency3;
public Service createService(Context context)
return new Service(context, dependency1, dependency2, dependency3);
1
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
1
@Dusan don't use Service Locator. IfServicehas dependencies other than theContext, that would not be provided by theClient, they can be provided via DI to theServiceFactoryto be passed to theServicewhencreateServiceis called.
– Mr.Mindor
May 2 at 19:53
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);and be left with your partially set up service, then later doService s = partial.context(context).build()
– Aaron
May 2 at 23:00
add a comment
|
There are several ways to deal with the initialization problem:
- As answered in https://softwareengineering.stackexchange.com/a/334994/301401, init() methods are a code smell. Initializing an object is the responsibility of the constructor - that's why we have constructors after all.
- Add The given service must be initialized to the doc comment of
Clientconstructor and let the constructor throw if the service is not initialized. This moves the responsibility to the one who gives you theIServiceobject.
However, in your example, the Client is the only one that knows the values that are passed to Initialize(). If you want to keep it that way, I'd suggest the following:
- Add an
IServiceFactoryand pass it to theClientconstructor. Then you can callserviceFactory.createService(new Context(...))which gives you an initializedIServicethat can be used by your client.
The factories can be very simple and also allow you to avoid init() methods and use constructors instead:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
public Service createService(Context context)
return new Service(context);
In the client, OnStartup() is also an initialization method (it just uses a different name). So if possible (if you know the Context data), the factory should directly be called in the Client constructor. If that's not possible, you need to store the IServiceFactory and call it in OnStartup().
When Service has dependencies not provided by Client they would be provided by DI through ServiceFactory:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public ServiceFactory(object dependency1, object dependency2, object dependency3)
this.dependency1 = dependency1;
this.dependency2 = dependency2;
this.dependency3 = dependency3;
public Service createService(Context context)
return new Service(context, dependency1, dependency2, dependency3);
1
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
1
@Dusan don't use Service Locator. IfServicehas dependencies other than theContext, that would not be provided by theClient, they can be provided via DI to theServiceFactoryto be passed to theServicewhencreateServiceis called.
– Mr.Mindor
May 2 at 19:53
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);and be left with your partially set up service, then later doService s = partial.context(context).build()
– Aaron
May 2 at 23:00
add a comment
|
There are several ways to deal with the initialization problem:
- As answered in https://softwareengineering.stackexchange.com/a/334994/301401, init() methods are a code smell. Initializing an object is the responsibility of the constructor - that's why we have constructors after all.
- Add The given service must be initialized to the doc comment of
Clientconstructor and let the constructor throw if the service is not initialized. This moves the responsibility to the one who gives you theIServiceobject.
However, in your example, the Client is the only one that knows the values that are passed to Initialize(). If you want to keep it that way, I'd suggest the following:
- Add an
IServiceFactoryand pass it to theClientconstructor. Then you can callserviceFactory.createService(new Context(...))which gives you an initializedIServicethat can be used by your client.
The factories can be very simple and also allow you to avoid init() methods and use constructors instead:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
public Service createService(Context context)
return new Service(context);
In the client, OnStartup() is also an initialization method (it just uses a different name). So if possible (if you know the Context data), the factory should directly be called in the Client constructor. If that's not possible, you need to store the IServiceFactory and call it in OnStartup().
When Service has dependencies not provided by Client they would be provided by DI through ServiceFactory:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public ServiceFactory(object dependency1, object dependency2, object dependency3)
this.dependency1 = dependency1;
this.dependency2 = dependency2;
this.dependency3 = dependency3;
public Service createService(Context context)
return new Service(context, dependency1, dependency2, dependency3);
There are several ways to deal with the initialization problem:
- As answered in https://softwareengineering.stackexchange.com/a/334994/301401, init() methods are a code smell. Initializing an object is the responsibility of the constructor - that's why we have constructors after all.
- Add The given service must be initialized to the doc comment of
Clientconstructor and let the constructor throw if the service is not initialized. This moves the responsibility to the one who gives you theIServiceobject.
However, in your example, the Client is the only one that knows the values that are passed to Initialize(). If you want to keep it that way, I'd suggest the following:
- Add an
IServiceFactoryand pass it to theClientconstructor. Then you can callserviceFactory.createService(new Context(...))which gives you an initializedIServicethat can be used by your client.
The factories can be very simple and also allow you to avoid init() methods and use constructors instead:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
public Service createService(Context context)
return new Service(context);
In the client, OnStartup() is also an initialization method (it just uses a different name). So if possible (if you know the Context data), the factory should directly be called in the Client constructor. If that's not possible, you need to store the IServiceFactory and call it in OnStartup().
When Service has dependencies not provided by Client they would be provided by DI through ServiceFactory:
public interface IServiceFactory
IService createService(Context context);
public class ServiceFactory : IServiceFactory
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public ServiceFactory(object dependency1, object dependency2, object dependency3)
this.dependency1 = dependency1;
this.dependency2 = dependency2;
this.dependency3 = dependency3;
public Service createService(Context context)
return new Service(context, dependency1, dependency2, dependency3);
edited May 2 at 21:40
Mr.Mindor
2992 silver badges8 bronze badges
2992 silver badges8 bronze badges
answered May 2 at 12:52
pschillpschill
4991 silver badge9 bronze badges
4991 silver badge9 bronze badges
1
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
1
@Dusan don't use Service Locator. IfServicehas dependencies other than theContext, that would not be provided by theClient, they can be provided via DI to theServiceFactoryto be passed to theServicewhencreateServiceis called.
– Mr.Mindor
May 2 at 19:53
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);and be left with your partially set up service, then later doService s = partial.context(context).build()
– Aaron
May 2 at 23:00
add a comment
|
1
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
1
@Dusan don't use Service Locator. IfServicehas dependencies other than theContext, that would not be provided by theClient, they can be provided via DI to theServiceFactoryto be passed to theServicewhencreateServiceis called.
– Mr.Mindor
May 2 at 19:53
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);and be left with your partially set up service, then later doService s = partial.context(context).build()
– Aaron
May 2 at 23:00
1
1
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
Thank you, just like I thought, in the last point... And in the ServiceFactory, would you use the constructor DI in the factory itself for the dependencies needed for the service constructor or the service locator would be more suitable?
– Dusan
May 2 at 12:58
1
1
@Dusan don't use Service Locator. If
Service has dependencies other than the Context, that would not be provided by the Client, they can be provided via DI to the ServiceFactory to be passed to the Service when createService is called.– Mr.Mindor
May 2 at 19:53
@Dusan don't use Service Locator. If
Service has dependencies other than the Context, that would not be provided by the Client, they can be provided via DI to the ServiceFactory to be passed to the Service when createService is called.– Mr.Mindor
May 2 at 19:53
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...
ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1); and be left with your partially set up service, then later do Service s = partial.context(context).build()– Aaron
May 2 at 23:00
@Dusan If you need to supply different dependencies to different Services (ie: this one needs dependency1_1 but the next one needs dependency1_2), but if this pattern otherwise works for you, then you can use a similar pattern often called a Builder pattern. A Builder allows you to set up an object piecemeal over time if necessary. Then you can do this...
ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1); and be left with your partially set up service, then later do Service s = partial.context(context).build()– Aaron
May 2 at 23:00
add a comment
|
The Initialize method should be removed from the IService interface, as this is an implementation detail. Instead, define another class that takes the concrete instance of Service and calls the initialize method on it. Then this new class implements the IService interface:
public class ContextDependentService : IService
public ContextDependentService(Context context, Service service)
this.service = service;
service.Initialize(context);
// Methods in the IService interface
This keeps client code ignorant of the initialization procedure, except where the ContextDependentService class is initialized. You at least limit the parts of your application that need to know about this wonky initialization procedure.
add a comment
|
The Initialize method should be removed from the IService interface, as this is an implementation detail. Instead, define another class that takes the concrete instance of Service and calls the initialize method on it. Then this new class implements the IService interface:
public class ContextDependentService : IService
public ContextDependentService(Context context, Service service)
this.service = service;
service.Initialize(context);
// Methods in the IService interface
This keeps client code ignorant of the initialization procedure, except where the ContextDependentService class is initialized. You at least limit the parts of your application that need to know about this wonky initialization procedure.
add a comment
|
The Initialize method should be removed from the IService interface, as this is an implementation detail. Instead, define another class that takes the concrete instance of Service and calls the initialize method on it. Then this new class implements the IService interface:
public class ContextDependentService : IService
public ContextDependentService(Context context, Service service)
this.service = service;
service.Initialize(context);
// Methods in the IService interface
This keeps client code ignorant of the initialization procedure, except where the ContextDependentService class is initialized. You at least limit the parts of your application that need to know about this wonky initialization procedure.
The Initialize method should be removed from the IService interface, as this is an implementation detail. Instead, define another class that takes the concrete instance of Service and calls the initialize method on it. Then this new class implements the IService interface:
public class ContextDependentService : IService
public ContextDependentService(Context context, Service service)
this.service = service;
service.Initialize(context);
// Methods in the IService interface
This keeps client code ignorant of the initialization procedure, except where the ContextDependentService class is initialized. You at least limit the parts of your application that need to know about this wonky initialization procedure.
answered May 2 at 12:57
Greg BurghardtGreg Burghardt
14.8k5 gold badges36 silver badges63 bronze badges
14.8k5 gold badges36 silver badges63 bronze badges
add a comment
|
add a comment
|
It seems to me that you have two options here
- Move the Initialisation code to the Context and inject an Initialised Context
eg.
public InitialisedContext Initialise()
- Have the first call to Execute call Initialise if its not allready done
eg.
public async Task Execute()
//lock context
//check context is not initialised
// init if required
//execute code...
- Just throw exceptions if Context isnt initialised when you call Execute. Like SqlConnection.
Injecting a factory is fine if you just want to avoid passing context as a parameter. Say only this particular implementation needs a context and you want not to add it to the Interface
But you essentially have the same problem, what if the factory hasn't got an initialised context yet.
add a comment
|
It seems to me that you have two options here
- Move the Initialisation code to the Context and inject an Initialised Context
eg.
public InitialisedContext Initialise()
- Have the first call to Execute call Initialise if its not allready done
eg.
public async Task Execute()
//lock context
//check context is not initialised
// init if required
//execute code...
- Just throw exceptions if Context isnt initialised when you call Execute. Like SqlConnection.
Injecting a factory is fine if you just want to avoid passing context as a parameter. Say only this particular implementation needs a context and you want not to add it to the Interface
But you essentially have the same problem, what if the factory hasn't got an initialised context yet.
add a comment
|
It seems to me that you have two options here
- Move the Initialisation code to the Context and inject an Initialised Context
eg.
public InitialisedContext Initialise()
- Have the first call to Execute call Initialise if its not allready done
eg.
public async Task Execute()
//lock context
//check context is not initialised
// init if required
//execute code...
- Just throw exceptions if Context isnt initialised when you call Execute. Like SqlConnection.
Injecting a factory is fine if you just want to avoid passing context as a parameter. Say only this particular implementation needs a context and you want not to add it to the Interface
But you essentially have the same problem, what if the factory hasn't got an initialised context yet.
It seems to me that you have two options here
- Move the Initialisation code to the Context and inject an Initialised Context
eg.
public InitialisedContext Initialise()
- Have the first call to Execute call Initialise if its not allready done
eg.
public async Task Execute()
//lock context
//check context is not initialised
// init if required
//execute code...
- Just throw exceptions if Context isnt initialised when you call Execute. Like SqlConnection.
Injecting a factory is fine if you just want to avoid passing context as a parameter. Say only this particular implementation needs a context and you want not to add it to the Interface
But you essentially have the same problem, what if the factory hasn't got an initialised context yet.
edited May 2 at 13:11
answered May 2 at 12:54
EwanEwan
48.3k3 gold badges48 silver badges108 bronze badges
48.3k3 gold badges48 silver badges108 bronze badges
add a comment
|
add a comment
|
You should not depend your interface to any db context and initialize method. You can do it in concrete class constructor.
public interface IService
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
private readonly object context;
public Service(
object dependency1,
object dependency2,
object dependency3,
object context )
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
// context is concrete class details not interfaces.
this.context = context;
// call init here constructor.
this.Initialize(context);
protected void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
And, an answer of your main question would be Property Injection.
public class Service
public Service(Context context)
this.context = context;
private Dependency1 _dependency1;
public Dependency1 Dependency1
get
if (_dependency1 == null)
_dependency1 = Container.Resolve<Dependency1>();
return _dependency1;
//...
This way you can call all dependencies by Property Injection. But it could be huge number. If so, you can use Constructor Injection for them, but you can set your context by property by checking if it is null.
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
add a comment
|
You should not depend your interface to any db context and initialize method. You can do it in concrete class constructor.
public interface IService
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
private readonly object context;
public Service(
object dependency1,
object dependency2,
object dependency3,
object context )
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
// context is concrete class details not interfaces.
this.context = context;
// call init here constructor.
this.Initialize(context);
protected void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
And, an answer of your main question would be Property Injection.
public class Service
public Service(Context context)
this.context = context;
private Dependency1 _dependency1;
public Dependency1 Dependency1
get
if (_dependency1 == null)
_dependency1 = Container.Resolve<Dependency1>();
return _dependency1;
//...
This way you can call all dependencies by Property Injection. But it could be huge number. If so, you can use Constructor Injection for them, but you can set your context by property by checking if it is null.
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
add a comment
|
You should not depend your interface to any db context and initialize method. You can do it in concrete class constructor.
public interface IService
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
private readonly object context;
public Service(
object dependency1,
object dependency2,
object dependency3,
object context )
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
// context is concrete class details not interfaces.
this.context = context;
// call init here constructor.
this.Initialize(context);
protected void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
And, an answer of your main question would be Property Injection.
public class Service
public Service(Context context)
this.context = context;
private Dependency1 _dependency1;
public Dependency1 Dependency1
get
if (_dependency1 == null)
_dependency1 = Container.Resolve<Dependency1>();
return _dependency1;
//...
This way you can call all dependencies by Property Injection. But it could be huge number. If so, you can use Constructor Injection for them, but you can set your context by property by checking if it is null.
You should not depend your interface to any db context and initialize method. You can do it in concrete class constructor.
public interface IService
void DoSomething();
void DoOtherThing();
public class Service : IService
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
private readonly object context;
public Service(
object dependency1,
object dependency2,
object dependency3,
object context )
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
// context is concrete class details not interfaces.
this.context = context;
// call init here constructor.
this.Initialize(context);
protected void Initialize(Context context)
// Initialize state based on context
// Heavy, long running operation
public void DoSomething()
// ...
public void DoOtherThing()
// ...
And, an answer of your main question would be Property Injection.
public class Service
public Service(Context context)
this.context = context;
private Dependency1 _dependency1;
public Dependency1 Dependency1
get
if (_dependency1 == null)
_dependency1 = Container.Resolve<Dependency1>();
return _dependency1;
//...
This way you can call all dependencies by Property Injection. But it could be huge number. If so, you can use Constructor Injection for them, but you can set your context by property by checking if it is null.
edited May 2 at 13:14
answered May 2 at 12:40
EngineertEngineert
8111 gold badge3 silver badges16 bronze badges
8111 gold badge3 silver badges16 bronze badges
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
add a comment
|
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
OK, great, but... each instance of the client needs to have it's own instance of the service initialized with different context data. That context data is not static or known beforehand so it cannot be injected by DI in the constructor. Then, how do I get/create instance of the service together with other dependencies in my clients?
– Dusan
May 2 at 12:48
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
hmm wont that static constructor run before you set the context? and initialize in the constructor risks exceptions
– Ewan
May 2 at 12:49
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
I am leaning towards injecting factory that can create and initialize the service with the given context data (rather than injecting service itself), but I am not sure if there are better solutions.
– Dusan
May 2 at 12:54
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
@Ewan You are right. I will try to find a solution for it. But before that, I will remove it for now.
– Engineert
May 2 at 12:57
add a comment
|
Misko Hevery has a very helpful blog post about the case you've faced. You both need newable and injectable for your Service class and this blog post may help you.
add a comment
|
Misko Hevery has a very helpful blog post about the case you've faced. You both need newable and injectable for your Service class and this blog post may help you.
add a comment
|
Misko Hevery has a very helpful blog post about the case you've faced. You both need newable and injectable for your Service class and this blog post may help you.
Misko Hevery has a very helpful blog post about the case you've faced. You both need newable and injectable for your Service class and this blog post may help you.
answered May 8 at 19:15
Bold P.Bold P.
243 bronze badges
243 bronze badges
add a comment
|
add a comment
|
Thanks for contributing an answer to Software Engineering Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f391290%2fhow-to-use-dependency-injection-and-avoid-temporal-coupling%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown