How to manage interface segregation when using an IoC container? [duplicate]

夙愿已清 提交于 2019-12-11 03:59:46

问题


Possible Duplicate:
StructureMap singleton usage (A class implementing two interface)

I'm currently designing a small system and i'm currently using structureMap as IoC. I just recently got the point of interface segregation...and I'm wondering now.

If I have a certain business object, that will implement say, three interfaces... how should I handle this in the configuration and instatiation of code?

Assuming I have two interfaces in a simple scenario, for a service layer class called EmployeeServiceObject. IGenericEntity and IEmployeeServiceObject.

GenericEntity will provide the CRUD capabilities for the class, and IEmployeeServiceObject will provide resultsets for business queries/operations.

If on a Facade/Service Layer method, I have to use the EmployeeServiceObject class and actually use functionality from both interfaces...how should this be handled?

Initially I thought that the correct thing was to setup the configuration of the IoC to map IEmployeeServiceObject to EmployeeServiceObject, ask the factory for the object, and just cast it to IGenericEntity when i needed to use the CRUD functionality, but i'm not quite sure. It also does not seem right because I'd never be formally stating that the concrete class is actually implementing the interface that was not setup in the ioc container configuration.

and I definitely know that creating two instances of the same concrete class but asking for a different interface...sounds even worse.

How should this be handled?


回答1:


If I understand the question correctly, you have the following scenario (in C# code):

public interface IGenericEntity { /**/ }

public interface IEmployeeServiceObject { /**/ }

public class EmployeeServiceObject : IEmployeeServiceObject, IGenericEntity

If so, the fact that EmployeeServiceObject implements both interfaces is an implementation detail. You may have other implementations that implement each interface separately.

The point of the ISP is that each interface models a separate concern, so if you often find yourself in a situation where you need both IGenericEntity and IEmployeeServiceObject you should question whether the separation into two interfaces is meaningful.

Otherwise you should request each interface separately, because any attempt at casting would be breaking the Liskov Substitution Principle.

In the cases where you truly need both, you will need to request both:

public class Foo
{
    public Foo(IGenericEntity ge, IEmployeeServiceObject eso) { /**/ }

    // ...
}

In the case where you have one class (EployeeServiceObject) implementing both interfaces, you will need to tell the DI Container that this is the case. At this point, we are moving in to the area of container-specific details, so how you do that varies with each container.

For example, Windsor has the Forward method that lets you specify that requests for one interface is forwarded to another type.

In Poor Man's DI it's as simple as

var eso = new EmployeeServiceObject();
var f = new Foo(eso, eso);

It's always a good thing to understand how you would compose the dependency hierarchy with Poor Man's DI because it can give you valuable clues to how the container would understand the same thing.




回答2:


If I may, before addressing the DI/IoC implementation, I would like to address your API. I think your pain points are trying to lead you to a better design.

The EmployeeServiceObject seems to have too many responsibilities. Consider the Single Responsibility Principle while creating your objects. In your description, the EmployeeServiceObject is both a facade/service layer and a CRUD layer. By assigning the interface for the CRUD operations(IGenericEntity), you are exposing your CRUD implementation to the consumer of your facade(IEmployeeServiceObject).

Given this situation and types you've described, please consider a different approach (I would also reconsider your type names to more accurately describe their purpose):

public interface IEmployeeServiceObject 
{
    // Service methods
    decimal GetSalary(string employeeId);
}

public class EmployeeServiceObject : IEmployeeServiceObject
{
    private IGenericEntity<Employee> _entity;

    public EmployeeServiceObject(IGenericEntity<Employee> entity)
    {
        _entity=entity;
    }

    public decimal GetSalary(string employeeId)
    {
        return _entity.Get(employeeId).Salary;
    }
}

public interface IGenericEntity<T>
{
    // CRUD Methods including...
    T Get(string id);
}

public class GenericEntity<T> : IGenericEntity<T> 
{
    T Get(string id){...}
}

Register the IEmployeeServiceObject/EmployeeServiceObject and the IGenericEntity/GenericEntity pairs with your container of choice. (StructureMap can register these types by convention.)

When you request the IEmployeeServiceObject from the container, the container will inject the GenericEntity into the EmployeeServiceObject's constructor prior to giving it to you.

In this way, you avoid the casting/registration issues you described. There is no need to force the container to work in a sub-optimal manner. Plus, your facade only exposes what is needed by its clients, and defers CRUD operations to another class (which does not need to be exposed to the clients).




回答3:


I didnt find how to close my question as duplicate...but I found exactly how to do what I needed in another stackoverflow question.

What Mark mentioned as the Forward method in Windsor, it also exists in StructureMap...and it appears it would be the right way to do things.

StructureMap singleton usage (A class implementing two interface)



来源:https://stackoverflow.com/questions/2594819/how-to-manage-interface-segregation-when-using-an-ioc-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!