I have a IItemProcessor
class which takes each item from a list and send it to a web API (IApiService
). The processing is done asynchronously, on another thread.
If web API responds with 'user not authorized', app needs to logout. For this, I have an IApp
which has LogoutUser()
method.
Currently, the ItemProcessor
implementation catches errors thrown by IApiService
and if the error is 'not authorized', it calls IApp:LogoutUser
.
The issue is in the implementation of IApp:LogoutUser
I need to call IItemProcessor:Stop
to stop processing. Which means there's a circular dependency between App
and `ItemProcessor.
I'm using Dependency Injection in the c-tor and this gives me infinite recursive construction of the concrete objects:
public class App : IApp
{
public App(IItemProcessor itemProcessor)
{
...
}
public void Logout()
{
_itemProcessor.Stop();
}
}
public class ItemProcessor : IItemProcessor
{
public ItemProcessor (IApp app)
{
...
}
public void Stop() {
...
}
void Run()
{
... for each item in list ..
try
{
... try send item ....
}
catch(UserNotAuthorizedException)
{
_app.Logout();
}
}
}
How can I fix this?
IApp
needs to know about IItemProcessor
to start and stop it on user login and logut. But I feel like IItemProcessor
breaks SRP, it shouldn't actually call Logout
, but rather somehow inform "someone" about the error, and that "someone" has in turn the responsability to call IApp:LogoutUser
. But what should this "someone" be? Should it be a listener to the item processor errors?
I'm having a hard time figuring out how to split responsibilities.
I tried to use command pattern by having a LogoutCommand
public class LogoutCommand
{
readonly IApp _app;
public LogoutCommand(IApp app)
{
_app = app;
}
public void Execute()
{
_app.UserLogout();
}
}
the ItemProcessor
implementation could call logoutCommand.Execute()
.
This would decouple ItemProcessor
from knowing about App
, but I have the same issue because when App
c-tor calls ItemProcessor
c-tor which calls LogoutCommand
which calls App
c-tor.
Another (bad) idea: I thought a factory could alleviate this issue. I thought having a ILogoutCommandFactory
factory which creates the LogoutCommand
. As long as I didn't create the LogoutCommand
in the c-tor of ItemProcessor
, I thought it could work. But the fact that I can't do that, feels to me like code smell.
To me DI in c-tor seems to start looking like an issue.
-
Does App tell IItemProcessor to process? Catch the notauthorized exception from the code the calls Run and call logout then. Alternately you can have a NotAuthorized event on the IItemProcessor interface. App hooks into it and calls Logout.Andy– Andy2017年05月02日 22:46:34 +00:00Commented May 2, 2017 at 22:46
1 Answer 1
Technically, there is no cyclic dependency. When I understood you correctly, App
depends on the interface IItemProcessor
, and not on its implementation ItemProcessor
. Also, ItemProcessor
depends on IApp
, and not on App
. However, you will need to switch to property injection for at least one of the classes, this will look like
public class App : IApp
{
public App(IItemProcessor itemProcessor)
{
itemProcessor.TheApp = this;
}
}
public class ItemProcessor : IItemProcessor
{
public ItemProcessor ()
{
...
}
public IApp TheApp{get;set;}
}
However, to decouple your components even more, or if your DI framework still has problems with this, IApp
could provide a publisher/subscriber mechanism (AKA observer pattern) for something like a "BeforeLogout" event. In a similar manner, IItemProcessor
could provide a publisher/subscriber mechanism for "not authorized" events it gets from the IApiService
. For example, in C# this could look like
interface IItemProcessor
{
void SubscribeToUserNotAuthorized(Action<Exception> observer);
}
and
public ItemProcessor() : IItemProcessor
{
List<Action<Exception>> notAuthorizedObservers;
public void SubscribeToUserNotAuthorized(Action<Exception> observer)
{
notAuthorizedObservers.Add(observer);
}
// and somewhere below
// ...
catch(NotAuthorizedException ex)
{
foreach(var observer in notAuthorizedObservers)
observer(ex);
}
}
-
ninja'ed. pub/sub observer is something i have use in the past and was typing up a reply with.RubberChickenLeader– RubberChickenLeader2017年05月02日 18:56:21 +00:00Commented May 2, 2017 at 18:56
-
I edited my question. Issue is I am using DI in c-tor which gives me infinite creation of the objects.Daniel– Daniel2017年05月02日 18:59:48 +00:00Commented May 2, 2017 at 18:59
-
What does "technically" mean? If technically there is no cyclic dependency, why does the DI gets in the way? I feel like cyclic dependency is a sign of bad design. Like I mentioned, I think in my case, the processor shouldn't know to call logout, it breaks SRP. Or maybe it doesn't? HELP! :)Daniel– Daniel2017年05月02日 19:15:47 +00:00Commented May 2, 2017 at 19:15
-
I edited it again. I tried to use command pattern but with same result.Daniel– Daniel2017年05月02日 19:31:31 +00:00Commented May 2, 2017 at 19:31
-
@DonBox: see my edit.Doc Brown– Doc Brown2017年05月02日 20:05:57 +00:00Commented May 2, 2017 at 20:05
Explore related questions
See similar questions with these tags.