2010-03-10 4 views
4

실제로 내가 코드의 일부분을 리팩터링합니다. 내가하고 싶은 일은 "TaskArgument"개체를 사용하여 "Task"개체를 초기화하는 것입니다. "TaskArgument"가 추상이고 "Task"가 "OnEnterTask (TaskArgument args)"메서드를 구현하고 (범위를 벗어난 기존 시스템의 일부 특수한 동작에 대해) 봉인되어 있다고 가정 해 보겠습니다.친절한 수업 #

이전 코드 :

public sealed class Task : SomeSystemBaseTask { 
    private int accessMe; 
    private int meToo; 

    public void OnEnterTask(TaskArgument args) { 
    if (args is SimpleTaskArgument) { 
     accessMe = ((SimpleTaskArgument)args).uGotIt; 
     meeToo = 0; 
    } else if (args is ComplexTaskArgument) { 
     accessMe = ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier; 
     meToo = ((ComplexTaskArgument)args).multiplier - 1; 
    } 
    } 
} 

은 무엇을 가장 좋은 방법은 유형 체킹을 피할 것입니까? 처음 stupud의 생각이었다 :

public abstract class TaskArgument { 
    internal public abstract Initialize(Task args); 
} 

public class SimpleTaskArgument : TaskArgument { 
    public int uGotIt = 10; 

    internal public Initialize(Task task){ 
     task.accessMe = uGotIt; 
    } 
} 

public class ComplexTaskArgument : TaskArgument { 
    public int uGotItValue = 10; 
    public int multiplier = 10; 

    internal public Initialize(Task task){ 
     task.accessMe = uGotItValue*multiplier; 
     task.meToo = multiplier - 1; 
    } 
} 

public sealed class Task : SomeSystemBaseTask { 
    public int accessMe; 
    public int meToo; 

    public void OnEnterTask(TaskArgument args){ 
     args.Initialize(this); 
    } 
} 

하지만 내 "accessMe는"공개하고 "초기화"방법은 "작업"에서만 작동합니다. 그래서 나는 typechecking을 다른 장소로 옮겼습니다 (장래에). 은 모범 사례 또는 좋은 디자인 아이디어입니다.

... "내부 공개"... mmhhmm?

또 다른 미친 아이디어는 내부 클래스했지만, 나는 그처럼 해달라고과 같은 간단한 경우는 더 복잡하거나하지 않습니다 : 그것의 기반이되는 경우

public abstract class TaskArgument { 
    internal public abstract Initialize(ITaskWrapper wrapper); 
} 

public class SimpleTaskArgument : TaskArgument { 
    ... 
} 

public class ComplexTaskArgument : TaskArgument { 
    ... 
} 

public interface ITaskWrapper { 
    public int AccessIt { set; get; } 
    ... 
} 

public sealed class Task : SomeSystemBaseTask { 
    private int accessMe; 
    ... 

    class TaskWrapper : ITaskWrapper { 
     ... 
    } 

    public void OnEnterTask(TaskArgument args){ 
     args.Initialize(new TaskWrapper(this)); 
    } 
} 

어디 초기화를위한 최고의 장소입니다 주어진 "TaskArgument"유형?

친절하게 내 나쁜 영어 지식

인사 당신이 그 클래스 위에 TaskArgument의 각 하위 클래스와 연관된 논리를 데려 가고 싶다는처럼

답변

9

인터페이스를 사용하십시오.

public void OnEnterTask(TaskArgument args) { 
    if (args is SimpleTaskArgument) { 
     accessMe = ((SimpleTaskArgument)args).uGotIt; 
    } else if (args is ComplexTaskArgument) { 
     accessMe = ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier; 
    } 
} 

당신이 당신의 클래스가 ITaskArgument를 구현하고, 각 클래스의 메소드를 구현해야 다음

public void OnEnterTask(ITaskArgument args) { 
    accessMe = args.GetAccessMe(); 
} 

된다. 일반적으로,이 같은 일을 할 때 : 당신이 계산을 수행하는 객체에 여러 속성을 액세스하는

accessMe = ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier;

, 그것은 일반적으로 클래스 자체에 그 논리를 밀어 의미가 있습니다.

+0

이 간단한 초기화에 적용됩니다. –

+0

@mo, 예 간단한 초기화를 위해 작동합니다. 그러나 위의 의존성 웹을 만드는 것처럼 보입니다. Task 클래스에서 TaskArg 객체를 가져 와서 TaskArg가 자신을 전달하는 곳 (this)에서 initialize를 호출하면 냄새라고 생각합니다. 그것은 어색한 양방향 의존성입니다. 당신의 염려를 분리하십시오! Task 및 TaskArgs에서 state를 사용하는 계산을 수행해야하는 경우 Task 및 TaskArgs를 상태 비즈니스 클래스로만 유지합니다 (최소 비즈니스 로직 사용). 그런 다음 둘 모두에 대해 알고있는 다른 클래스를 만들고 (거기에 대해서는 알지 못함) 거기에서 계산을 수행하십시오. 내게는 – manu08

+0

이 맞습니다 :) 가장 깨끗한 방법 일 수 있습니다. –

3

는 소리 개월 변명. 서브 클래스 별 계산이있는 Calculate이라는 TaskArgument에 추상 메소드를 추가 할 수 있습니다. 그러면 if 문이 완전히 필요하지 않게됩니다.

 
public class Task { 
    private int accessMe;

public void OnEnterTask(TaskArgument args) { accessMe = args.Calculate(); } }

그러면 각 하위 클래스에 적절한 곱셈을 넣을 수 있습니다.

+0

한 VAR에 대해 큰 소리/property.but 복잡한 초기화의 경우에는? ComplexTaskArgument는 Task의 다른 var/propery에 대한 데이터를 제공 할 수 있습니다. 이 케이스를 제 질문에 추가 할 것입니다. –

+1

@ 모오, 그럼 당신이 모델링하는 방법을 다시 생각해 봐야합니다. Task 및 TaskArgument에서 상태가 필요한 항목을 계산해야하는 경우 해당 계산은 둘 다 알고있는 별도의 클래스에서 수행되어야합니다. – manu08

+0

좋아, 나는 내가 좋은 인터페이스 디자인과 함께가는 것 같아. –

1

좋아, 의견에 나타나는 변화하는 요구 사항에 비추어 조금 대답을 바 꾸었습니다! (Sheesh, scope creep 또는 무엇?)

public class Task 
{ 
    public int Variable1 { get; internal set; } 
    public int Variable2 { get; internal set; } 

    public void OnEnterTask(ITaskInitializer initializer) 
    { 
     initializer.Initialize(this); 
    } 
} 

public interface ITaskInitializer 
{ 
    void Initialize(Task task); 
} 

public class SimpleTaskInitializer : ITaskInitializer 
{ 
    private int uGotIt = 10; 

    public void Initialize(Task task) 
    { 
     task.Variable1 = uGotIt; 
    } 
} 

public class ComplexTaskInitializer : ITaskInitializer 
{ 
    private int uGotIt = 10; 
    private int multiplier = 10; 

    public void Initialize(Task task) 
    { 
     task.Variable1 = uGotIt; 
     task.Variable2 = uGotIt * multiplier; 
     // etc - initialize task however required. 
    } 
} 
+0

@Ian : TaskArgument 대신 ITaskArgument를 취하기 위해 Task.OnEnterTask 메서드를 수정해야합니다. – manu08

0

당신은 하나의 옵션으로 작업의 오버로드를 만들 수 있습니다 상응하는 인수 유형

public class SimpleTask : Task 
{ 
    public override void EnterTask(TaskArgument arg) 
    { 
     var s = (SimpleTaskArgument)arg; 
    } 
} 

그래서 각 작업 유형의 거래를. 또는 int를 반환하는 정적 메서드로 TaskFactory로 논리를 이동하고 거기에 형식 검사 인수가있을 수 있습니다.

public static class TaskFactory 
{ 
    public static int GetVal(TaskArgument arg) 
    { 
     if (args is SimpleTaskArgument) { 
     return ((SimpleTaskArgument)args).uGotIt; 
     } else if (args is ComplexTaskArgument) { 
     return ((ComplexTaskArgument)args).uGotItValue * ((ComplexTaskArgument)args).multiplier; 
     } 
    } 
} 

인터페이스 구현도 가능합니다. 나는 그것을 할인하지 않거나 Taskargument 내에서 abstract 메소드를 정의하여 각각이 값을 반환하도록 재정의합니다.

HTH.

+0

어쩌면 "Task"라는 이름이 자극을 줄 수 있습니다. 나는 그것을 봉인 된 것으로 정의 할 것이다. –

1

공개 인터페이스를 만들면 Intialize 메서드 만 노출됩니다. 예를 들어 파생 클래스에서 계산을 수행합니다.

public interface ITaskArgument 
{ 
    void Initialize(Task task); 
} 

public abstract class TaskArgument : ITaskArgument 
{ 
    protected int _value; 
    public class TaskArgument(int value) 
    { 
     _value = value; 
    } 

    public abstract void Initialize(Task task); 
} 

public class SimpleTaskArgument : TaskArgument, ITaskArgument 
{ 
    public SimpleTaskArgument(int value) 
     : base (value) 
    { 
    } 

    public override void Initialize(Task task) 
    { 
     task.AccessMe = _value; 
    } 
} 

public class ComplexTaskArgument : TaskArgument, ITaskArgument 
{ 
    private int _multiplier; 

    public ComplexTaskArgument(int value, int multiplier) 
     : base (value) 
    { 
     _multiplier = multiplier; 
    } 

    public override void Initialize(Task task) 
    { 
     task.AccessMe = _value * _multiplier; 
    } 
} 

public class Task 
{ 
    public Task() 
    { 
    } 

    public int AccessMe { get; set; } 

    public void OnEnterTask(ITaskArgument args) 
    {       
     args.Initialize(this);       
    } 
} 

예를

SimpleTaskArgument simpleArgs = new SimpleTaskArgument(10); 
ComplexTaskArgument complexArgs = new ComplexTaskArgument(10, 3); 
Task task = new Task(); 
task.OnEnterTask(simpleArgs); 
Console.WriteLine(task.AccessMe); // would display 10 
task.OnEnterTask(complexArgs); 
Console.WriteLine(task.AccessMe); // would display 30