5

我有我的代碼,使web服務基於請求的類型調用。如何解決類型在運行時,以避免multipe如果其他

要做到這一點,我有以下代碼;

public class Client 
{ 
    IRequest request; 


    public Client(string requestType) 
    { 
     request = new EnrolmentRequest(); 
     if (requestType == "Enrol") 
     { 
      request.DoEnrolment(); 
     } 
     else if (requestType == "ReEnrol") 
     { 
      request.DoReEnrolment(); 
     } 
     else if (requestType == "DeleteEnrolment") 
     { 
      request.DeleteEnrolment(); 
     } 
     else if (requestType == "UpdateEnrolment") 
     { 
      request.UpdateEnrolment(); 
     } 
    } 

} 

所以按照開閉原則,我可以繼承,如:

Class EnrolmentRequest:IRequest 
{ 
    CallService(); 
} 
Class ReEnrolmentRequest:IRequest 
{ 
    CallService(); 
} 
Class UpdateEnrolmentRequest:IRequest 
{ 
    CallService(); 
} 

現在我的客戶端類將是這個樣子:

public class Client 
{ 
    public Client(string requestType) 
    { 
     IRequest request; 

     if (requestType == "Enrol") 
     { 
      request = new EnrolmentRequest(); 
      request.CallService(); 
     } 
     else if (requestType == "ReEnrol") 
     { 
      request = new REnrolmentRequest(); 
      request.CallService(); 
     } 
     else if (requestType == "DeleteEnrolment") 
     { 
      request = new UpdateEnrolmentRequest(); 
      request.CallService(); 
     } 
     else if (requestType == "UpdateEnrolment") 
     { 
      request = new UpdateEnrolmentRequest(); 
      request.CallService(); 
     } 
    } 

} 

現在,我仍然要使用if和else,並且如果有任何新的請求類型,將不得不更改我的代碼。

所以,它絕對沒有關閉修改。

我是否缺少與SOLID有關的任何內容?

我可以使用依賴注入來解析運行時的類型嗎?

+0

'requestType'從哪裏來?它是否爲'request.GetType()'的僞代碼? – InBetween

+0

也許這應該更好地發佈在http://codereview.stackexchange.com/ –

+0

什麼類的實例化的決定必須在某個地方 - 沒有得到解決。決定可以轉移到使用反射或註冊的工廠,但不能消除。 –

回答

4

您可以添加簡單的工廠類象下面這樣:

public class ServiceFactory : Dictionary<string, Type> 
{ 
    public void Register(string typeName, Type serviceType) { 
     if (this.ContainsKey(typeName)) { 
      throw new Exception("Type registered"); 
     } 
     this[typeName] = serviceType; 
    } 

    public IRequest Resolve(string typeName) { 
     if (!this.ContainsKey(typeName)) { 
      throw new Exception("Type not registered"); 
     } 
     var type = this[typeName]; 
     var service = Activator.CreateInstance(type); 
     return service as IRequest; 
    } 
} 

然後在一個地方註冊服務,如:

var serviceFactory = new ServiceFactory(); 
     serviceFactory.Register("Enrol", typeof(EnrolmentRequest)); 
     serviceFactory.Register("ReEnrol", typeof(REnrolmentRequest)); 
     serviceFactory.Register("DeleteEnrolment", typeof(UpdateEnrolmentRequest)); 
     serviceFactory.Register("UpdateEnrolment", typeof(UpdateEnrolmentRequest)); 

,並稱之爲:

var service = serviceFactory.Resolve(requestType); 
service.CallService(); 

還需要添加合適的錯誤處理

4

問得好, 你可以使用一個單一的方法實現自己的目標:

var request = (IRequest)Activator.CreateInstance("NameOfYourAssembly", requestType); 
request.CallService(); 

思考將幫助你生成你的類的實例。之後你可以在沒有if/else的情況下調用它。

請參閱此鏈接瞭解提供的方法的詳細信息:https://msdn.microsoft.com/it-it/library/3k6dfxfk(v=vs.110).aspx

希望這可以幫助

+0

請注意,根據本頁上的其他答案,'CreateInstance()'比相當的'new EnrolmentRequest()慢**幾個數量級** ** – MickyD

+0

Thanks @MickyD,你是對的。我剛剛提供了一種處理這種情況的方法。在這種情況下,我們並不是在談論性能,無論如何,您的信息都是選擇如何處理代碼聲明的基礎。 –

+0

同意。先生,不是問題。 :) – MickyD

8

需要編寫新的代碼來處理新的需求是不會消失的。目標是在處理新需求時不必更改舊代碼,並且您的類結構會處理它。

您可以通過用創建新實例的其他一些機制替換條件鏈來最小化更改。例如,您可以構建字典,或使用依賴注入框架將類型與字符串相關聯。

這裏是不使用DI框架的實現:

private static readonly IDictionary<string,Func<IRequest>> ReqTypeMapper = 
    new Dictionary<string,Func<IRequest>> { 
     {"Enrol",() => new EnrolmentRequest() } 
    , {"ReEnrol",() => new ReEnrolmentRequest() } 
    , ... 
    }; 

現在通話將這個樣子:

Func<IRequest> maker; 
if (!ReqTypeMapper.TryGetValue(requestType, out maker)) { 
    // Cannot find handler for type - exit 
    return; 
} 
maker().CallService(); 
5

你真的無法刪除的if列表 - 完全case語句,除非你恢復 - elseswitch使用反射。在系統的某個地方,你肯定會進行某種調度(使用硬編碼列表或反射)。

你的設計卻可以從一個更基於消息的做法是,如果進來的請求是消息,如受益:

class DoEnrolment { /* request values */ } 
class DoReenrolment { /* request values */ } 
class DeleteEnrolment { /* request values */ } 
class UpdateEnrolment { /* request values */ } 

這允許你創建一個單一的界面defenition這種要求的「處理程序」:

interface IRequestHandler<TRequest> { 
    void Handle(TRequest request); 
} 

你的處理程序將如下所示:

class DoEnrolmentHandler : IRequestHandler<DoEnrolment> { 
    public void Handle(DoEnrolment request) { ... } 
} 

class DoReenrolmentHandler : IRequestHandler<DoReenrolment> { 
    public void Handle(DoReenrolment request) { ... } 
} 

class DeleteEnrolmentHandler : IRequestHandler<DeleteEnrolment> { 
    public void Handle(DeleteEnrolment request) { ... } 
} 

這樣做的好處是,應用橫切關注是一件輕而易舉的事情,因爲定義一個實現類似日誌記錄的通用裝飾器是非常簡單的。

這當然還是帶我們回到調度。調度可以從客戶端中提取,後面自己的抽象:

interface IRequestDispatcher { 
    void Dispatch<TRequest>(TRequest request); 
} 

這使得客戶端只需發送它需要請求:

// Client 
this.dispatcher.Dispatch(new DoEnrolment { EnrolId = id }); 

請求調度員可能是這樣的實現:

class ManualRequestDispatcher : IRequestDispatcher { 
    public void Dispatch<TRequest>(TRequest request) { 
     var handler = (IRequestHandler<TRequest>)CreateHandler(typeof(TRequest)); 
     handler.Handle(request); 
    } 

    object CreateHandler(Type type) => 
     type == typeof(DoEnrolment)? new DoEnrolmentHandler() : 
     type == typeof(DoReenrolment) ? new DoReenrolment() : 
     type == typeof(DeleteEnrolment) ? new DeleteEnrolment() : 
     type == typeof(UpdateEnrolment) ? new UpdateEnrolment() : 
     ThrowRequestUnknown(type); 

    object ThrowRequestUnknown(Type type) { 
     throw new InvalidOperationException("Unknown request " + type.Name); 
    } 
} 

但是如果你使用一個DI容器,你將能夠與東西批量註冊請求處理如下(取決於您使用O庫當然˚F):

container.Register(typeof(IRequestHandler<>), assemblies); 

而且你可能調度員看起來如下:

class ContainerRequestDispatcher : IRequestDispatcher { 
    private readonly Container container; 
    public ContainerRequestDispatcher(Container container) { 
     this.container = container; 
    } 

    public void Dispatch<TRequest>(TRequest request) { 
     var handler = container.GetInstance<IRequestHandler<TRequest>>(); 
     handler.Handle(request); 
    } 
} 

您可以找到有關這種類型的設計herehere的更多信息。

+1

標記Sergie的答案,因爲它很容易遵循。你的答案是一流的,需要一些時間來理解。 Thaks的解決方案 – Simsons

+0

「你完全不能真的刪除if-else或switch-case語句的列表,除非你恢復使用反射。」真的嗎?請參閱dasblinkenlights答案。 – weston

+1

@weston:請注意,我正在討論選項的「硬編碼列表」。字典是一個硬編碼列表,就像'if'-'else'和'switch'''case'一樣。 – Steven

0

可以使用autofac keyed or named service..

public enum OperationType 
{ 
    Enrol, 
    ReEnrol, 
    DeleteEnrolment, 
    UpdateEnrolment 
} 

     //register types 
     builder.RegisterType<EnrolmentRequest>().Keyed<IRequest>(OperationType.Enrol); 
     builder.RegisterType<ReEnrolmentRequest>().Keyed<IRequest>(OperationType.ReEnrol); 
     builder.RegisterType<UpdateEnrolmentRequest>().Keyed<IRequest>(OperationType.DeleteEnrolment | OperationType.UpdateEnrolment); 


     // resolve by operationType enum 
     var request = container.ResolveKeyed<IRequest>(OperationType.Enrol); 
3

您可以使用工廠模式,與RIP(替換如果與多態性)避免多次if-else

下面的代碼是根據您的Client類的示例代碼:

public enum RequestType : int 
{ 
    Enrol = 1, 
    ReEnrol, 
    UpdateEnrolment 
} 

public interface IRequest 
{ 
    void CallService(); 
} 

public class EnrolmentRequest : IRequest 
{ 
    public void CallService() 
    { 
     // Code for EnrolmentRequest 
    } 
} 

public class ReEnrolmentRequest : IRequest 
{ 
    public void CallService() 
    { 
     // Code for ReEnrolmentRequest 
    } 
} 

public class UpdateEnrolmentRequest : IRequest 
{ 
    public void CallService() 
    { 
     // Code for UpdateEnrolmentRequest 
    } 
} 

// Factory Class 
public class FactoryChoice 
{ 
    private IDictionary<RequestType, IRequest> _choices; 

    public FactoryChoice() 
    { 
     _choices = new Dictionary<RequestType, IRequest> 
      { 
       {RequestType.Enrol, new EnrolmentRequest() }, 
       {RequestType.ReEnrol, new ReEnrolmentRequest()}, 
       {RequestType.UpdateEnrolment, new UpdateEnrolmentRequest()} 
      }; 
    } 

    static public IRequest getChoiceObj(RequestType choice) 
    { 
     var factory = new FactoryChoice(); 

     return factory._choices[choice]; 
    } 
} 

,它會被調用,如:

IRequest objInvoice = FactoryChoice.getChoiceObj(RequestType.ReEnrol); 
objInvoice.CallService(); 

在這裏,主要的事情發生在FactoryChoice類的構造函數。這就是爲什麼有人稱它爲智能構造函數。這樣,您可以避免多行程if-elseswitch-case

要知道的基本RIP你可以檢查我的幻燈片here

相關問題