0

클라이언트에서 일괄 처리하고 서버에서 실행해야하는 많은 명령이 있습니다. 이 명령은 다른 유형이며 명령 및 해당 리턴 유형에 대한 계약은 라이브러리를 통해 클라이언트와 서버간에 공유됩니다. 각각에 대해디자인 패턴 - 클라이언트 서버 - 명령 패턴

List<CommandResult> Execute(List<CommandBase> commands) 
{ 
    List<CommandResult> results = new List<CommandResult>(); 
    foreach(CommandBase command in commands) 
    { 
     if(command.GetType == Command1) 
     { 
      results.Add(new Command1Executor(command).Execute()) 
     } 
     else if(command.GetType == Command2) 
     { 
      results.Add(new Command1Executor(command).Execute()) 
     } 
     else if(command.GetType == Command3) 
     { 
      results.Add(new Command3Executor(command).Execute()) 
     }  
     ..................  
    } 
} 

고유 실행 기능, 즉이 클라이언트 SDK의 일부로 노출 할 수없는이 존재 명령 -

클라이언트 측 코드는

var client = new ClientSDK(); 
client.Add(new Command1()); 
client.Add(new Command2()); 
client.Add(new Command3()); 

// Execute transmits all the commands to the server 
var results = client.Execute(); 

follows- 등의 서버 코드입니다. 방대한 if/else 블록을 제거 할 수 있도록 설계 변경을 어떻게해야합니까? 많은 명령이 지원됩니다. 여기에 제안 된대로 명령 패턴을 적용 해 보았습니다. 즉, using the command and factory design patterns for executing queued jobs이지만 ICommand 인터페이스를 구현하는 데는 각 명령이 필요합니다. 이는 불가능합니다.

더 좋은 방법이 있습니까?

+0

명령 유형에 따라 키가 사전에 (을 만들거나 functons) 모든 명령 집행을 넣습니다. 이렇게하면 다른 체인이 필요하지 않습니다. – Evk

답변

1

기본적으로 CommandNExcecutor 유형을 CommandN 유형으로 매핑해야합니다.

1) 사전을 사용하십시오. 이것은 가장 쉬운 방법입니다.

private static readonly Dictionary<Type, Type> map = new Dictionary<Type, Type> 
{ 
    { typeof(Command1), typeof(Command1Executor) }, 
    { typeof(Command2), typeof(Command2Executor) }, 
    ... 
}; 

List<CommandResult> Execute(List<CommandBase> commands) 
{ 
    return commands 
     .Select(command => 
     { 
      var executor = Activator.CreateInstance(map[command.GetType], command); 
      return executor.Execute(); 
     }) 
     .ToList(); 
} 

2) 메타 데이터 (속성)를 사용하십시오. 이 기능은 핵심 기능을 다시 빌드하지 않고 명령 유형을 동적으로 추가 할 수있는 플러그인 기반 시나리오에 적합합니다. 그것은 당신 자신의 구현 일 수도 있고 기존 DI 컨테이너 구현 일 수도 있습니다 (대다수는 메타 데이터 API를 공개합니다).

[AttributeUsage(AttributeTargets.Class)] 
public sealed class CommandExecutorAttribute : Attribute 
{ 
    public CommandExecutorAttribute(Type commandType) 
    { 
     CommandType = commandType; 
    } 

    public Type CommandType { get; } 

    // ... 
} 

[CommandExecutor(typeof(Command1))] 
public sealed class Command1Executor : ICommandExecutor 
{ 
    // ... 
} 

List<CommandResult> Execute(List<CommandBase> commands) 
{ 
    return commands 
     .Select(command => 
     { 
      // obtain executor types somehow, e.g. using DI-container or 
      // using reflection; 
      // inspect custom attribute, which matches command type 
      var executorType = .... 

      var executor = Activator.CreateInstance(executorType , command); 
      return executor.Execute(); 
     }) 
     .ToList(); 
} 

UPDATE.

당신은 반사를 피하기 첫 번째 경우에 단지 Func<CommandBase, ICommandExecutor>에 사전에 값을 입력 매개 변수를 바꾸려면 :

var executor = map[command.GetType](command); 
:

private static readonly Dictionary<Type, Func<ICommandExecutor>> map = new Dictionary<Type, Func<ICommandExecutor>> 
    { 
     { typeof(Command1), command => new Command1Executor(command) }, 
     { typeof(Command2), command => new Command2Executor(command) }, 
     ... 
    }; 

이 대신 반사의 위임을 통해 집행을 만들 수 있습니다

두 번째 경우 반사를 완전히 피할 수 없습니다. 실행자 유형을 어떻게 든 가져와야하기 때문입니다. 그러나 사례 1 (사전 포함)로 이어질 수 있습니다.

게으른 map 확인 :

private static readonly Lazy<Dictionary<Type, ConstructorInfo>> map = ... 

그런 다음, Lazy<T> 초기에, 모든 반사 작업을 수행. 이 번호는 static Lazy<T>이므로 앱 도메인별로 한 번 수행합니다. ConstructorInfo 전화는 충분히 빠릅니다. 따라서 첫 번째 명령을 처리 할 때 한 번만 성능이 저하됩니다.

경우 2에 대한 또 다른 옵션은 (그들은 모두 Lazy<Dictionary> 가정)입니다

대신 ConstructorInfo을 반영
  • , ConstructorInfo를 사용하여 Expression<Func<CommandBase, ICommandExecutor>>의 구축을 컴파일하고 사전에 대표단을 넣어 -이의 대리인과 동일합니다 case 1이지만 동적으로 지원되는 명령 유형;
  • IL을 방출하여 종속성을 생성하는 DI 컨테이너를 사용합니다 (AFAIK, NInject).
  • 자신을 내 보냅니다 (IMO, 이것은 바퀴를 완전히 발명 할 것입니다).

그리고 마지막으로,이 가장 쉬운 방법 해결 측정 성능, 더 복잡한 방법을 고려한다. 조숙 한 최적화를 피하십시오. 나는 당신이 자연을 명령하는 것에 대해서는 아무 것도 모른다. 그러나 그 명령 실행은 보다 길다. 뭔가를 반영하는 것보다 길다. 물론 나는 틀렸다는 것을 알 수있다.

희망이 도움이됩니다.

+0

성능 우선 순위이므로 Activator.CreateInstance 및 Reflection을 피하고 싶습니다. – user1542794

+0

@ user1542794 : 사실 반성은 대답의 주요 포인트가 아닙니다. 하지만 코드를 빠르게 만드는 방법을 알려주기 위해 업데이트했습니다. – Dennis

+0

Dennis에게 감사드립니다. 귀하의 솔루션은 문제를 아주 깨끗하게 해결합니다. – user1542794

0

전략 패턴을 사용해보십시오. 간단한 해결책은 다음과 같습니다 :

public class CommandStrategy 
{ 
    private static Dictionary<CommandTypes, Action<CommandStrategy>> strategy; 

    public CommandStrategy() 
    { 
     strategy = new Dictionary<CommandTypes, Action<CommandStrategy>>(); 
     strategy.Add(CommandTypes.Command1, one => new Command1Executor().Execute()); 
     strategy.Add(CommandTypes.Command2, two => new Command2Executor().Execute()); 
     strategy.Add(CommandTypes.Command3, two => new Command3Executor().Execute()); 
    } 

    public void Execute(CommandTypes type) 
    { 
     strategy[type].Invoke(this); 
    } 
} 

마지막 실행은 다음과 같습니다

CommandStrategy strategy = new CommandStrategy(); 

List<CommandBase> commands = new List<CommandBase>(){ 
        new Command1(), new Command2(), new Command3() }; 

foreach (var item in commands) 
{ 
    CommandTypes type = (CommandTypes)item; 
    strategy.Execute(type); 
} 
+0

패턴을 사용해 보았지만 다른 문제가 발생했습니다. 일단 NewtonSoft.Json 라이브러리를 사용하여 명령을 직렬화하면 서버 측에서 직렬화되지 않는 문제가 발생합니다. 몇 가지 솔루션을 시도하면 게시됩니다. – user1542794