다형 객체를 설계하는 가장 좋은 방법
-
08-07-2019 - |
문제
데이터 객체가 있지만이 객체는 여러 유형의 데이터 중 하나를 보유 할 수 있습니다.
class Foo
{
int intFoo;
double doubleFoo;
string stringFoo;
}
이제 액세서를 만들고 싶습니다. 이 데이터를 얻는 방법. 분명히 여러 액세서를 만들 수 있습니다.
public int GetIntFoo();
public double GetDoubleFoo();
public string GetStringFoo();
또는 여러 속성을 만들 수 있습니다
public int IntFoo { get; set; }
public double DoubleFoo { get; set; }
public string StringFoo { get; set; }
나는 이것이 아주 좋은 디자인이라는 것이 아닙니다. 클라이언트 코드는 유형에 대해 더 걱정해야합니다. 또한,이 클래스에는 단일 값 만 필요하며 위는 각 유형 중 하나를 동시에 할당 할 수 있습니다. 안좋다.
한 가지 옵션은 제네릭을 사용하는 것입니다.
class Foo<T>
{
public T TheFoo { get; set; }
}
그러나 이것은 foo를 만들지 않고 foo를 만듭니다.u003CT> . 각각마다 다른 유형이므로 실제로 동일한 유형으로 사용할 수 없습니다.
나는 foo를 도출 할 수 있습니다u003CT> Foobase에서 모든 것을 Foobase 's로 취급하지만 데이터에 액세스하는 문제로 돌아 왔습니다.
다른 제네릭 옵션은 다음과 같은 것을 사용하는 것입니다.
class Foo
{
string stringRepresentationOfFoo;
public T GetFoo<T>() { return /* code to convert string to type */ }
}
물론 문제는 모든 종류의 T가 통과 될 수 있다는 것입니다. 솔직히 말해서 조금 바쁩니다.
나는 또한 값을 상자에 넣고 객체를 반환 할 수 있지만 유형 안전은 없습니다.
이상적으로는 모든 Foo를 동일하게 취급하고 싶지만 StringFoo가 없으면 Stringfoo에 대한 참조를 컴파일 할 수 없도록 유형 안전을 원합니다.
Foo foo = new Foo("Foo");
string sFoo = foo.Value; // succeeds.
Foo foo = new Foo(0);
int iFoo = foo.Value; // succeeds
string sFoo = foo.Value; // compile error
아마도 이것은 불가능할 것입니다. 그리고 나는 약간의 타협을해야 할 것입니다.
어떤 아이디어?
편집하다:
Daniel이 지적했듯이 런타임 유형의 컴파일 시간 점검은 실용적이지 않습니다.
내가 여기서하고 싶은 일을하기위한 최선의 선택은 무엇입니까? 즉, 모든 foo를 동일하게 취급하지만 여전히 비교적 제정신 접근 메커니즘을 가지고 있습니까?
edit2 :
값을 다른 유형으로 변환하고 싶지 않습니다. 값에 대한 올바른 유형을 반환하고 싶습니다. 즉, 두 배라면 int를 반환하고 싶지 않습니다.
해결책
변수를 Get의 매개 변수로 전달하는 것은 어떻습니까? 이와 같이:
int i = foo.get(i);
그런 다음 수업에는 다음과 같은 것이 있습니다.
public int get(int p) {
if(this.type != INTEGER) throw new RuntimeException("Data type mismatch");
return this.intVal;
}
public float get(float p) {
if(this.type != FLOAT) throw new RuntimeException("Data type mismatch");
return this.floatVal;
}
이런 종류의 유형은 내부를 확인하는 유형을 바꿉니다. Foo가 어떤 유형의 보유가 보유하고 있는지 확인하는 대신 Foo가 원하는 유형을 확인하십시오. 해당 유형을 줄 수 있다면, 그렇지 않으면 런타임 예외를 던집니다.
다른 팁
나는 이것이 효과가 있다고 생각하지 않습니다 (원하는 컴파일러 오류를 제공합니다)
당신은 이것을 무엇을하기를 원하십니까 :
Foo bar = (new Random()).Next(2) == 0 ? new Foo("bar") : new Foo(1);
int baz = bar.Value;
컴파일러 오류입니까?
나는 "그들 모두를 동일하게 처리"(적어도 당신이 설명한 방식)과 "컴파일 타임 오류"는 상호 배타적이라고 생각합니다.
어쨌든, 나는 "가장 좋은 방법"이 제네릭과 상속 사이의 타협이 될 것이라고 생각합니다. 당신은 a를 정의 할 수 있습니다 Foo<T>
그것은 foo의 서브 클래스입니다. 그런 다음 여전히 컬렉션을 가질 수 있습니다 Foo
.
abstract public class Foo
{
// Common implementation
abstract public object ObjectValue { get; }
}
public class Foo<T> : Foo
{
public Foo(T initialValue)
{
Value = initialValue;
}
public T Value { get; set; }
public object ObjectValue
{
get { return Value; }
}
}
많은 시스템은 .NET Frameworks Base Object에 toString () 메소드가있는 것처럼 대체 유형을 반환하기 위해 도우미 방법을 사용합니다.
각 객체에 가장 적합한 기본 유형을 선택하고 다른 경우 방법을 제공합니다.
예를 들어
class Foo{
public Int32 Value { get; set; }
public Byte ToByte() { return Convert.ToByte(Value); }
public Double ToDouble() { return (Double)Value; }
public new String ToString() { return Value.ToString("#,###"); }
}
한 가지는 클래스의 내부 상태에 모든 유형을 저장하는 것이고 다른 하나는 외부에서 노출하는 것입니다. 수업을 작성할 때 실제로 그 행동에 대한 계약을 선언하는 것입니다. 작성 방식은 클래스를 사용할 때 클라이언트 코드의 모습에 큰 영향을 미칩니다.
예를 들어, 상상할 수 있습니다 인터페이스 당신은 유형을 CLR 유형으로 동등한 값으로 변환 할 수 있다고 말합니다.
또한 값 클래스가 계산 결과를 저장하는 데 사용되는 구현, 문자열, 이중, int 또는 부울을 나타내는 결과를 보았습니다. 그러나 문제는 클라이언트 코드가 Enum {String, Integer, Double, Boolean}의 값을 확인한 다음 값을 캐스트해야한다는 것입니다. ) 또는 특정 용도, 가치, ValueInt, ValueBoolean Getters를 사용하십시오.
그냥 사용하지 않는 이유는 무엇입니까? string
, double
그리고 int
?
수집에 대한 정보 후 : 사용은 어떻습니까 object
? 어쨌든 유형 등을 확인해야합니다. 그리고 당신을 도울 수 있도록 is
그리고 as
운영자. 그리고 열거 가능 .cast 메소드, 또는 더 나은 열거 가능. ofType 방법.
사실,이 수업의 목적은 무엇입니까? 가장 큰 문제는 적어도 SRP (단일 책임 원리)를 깨는 디자인 인 것 같습니다.
그럼에도 불구하고, 내가 올바르게 읽고 있다면 컨테이너에 값을 저장하고 컨테이너를 클라이언트에 전달하고 값을 유형으로 검색하고 싶습니다.
이 접근법을 사용하면 제안서를 사용할 수 있습니다.
namespace Project1 {
public class Class1 {
static int Main(string[] args) {
Foo a = new Foo();
a.SetValue(4);
Console.WriteLine(a.GetValue<int>());
Foo b = new Foo();
a.SetValue("String");
Console.WriteLine(a.GetValue<string>());
Console.ReadLine();
return 0;
}
}
class Foo {
private object value; // watch out for boxing here!
public void SetValue(object value) {
this.value = value;
}
public T GetValue<T>() {
object val = this.value;
if (val == null) { return default(T); } // or throw if you prefer
try {
return (T)val;
}
catch (Exception) {
return default(T);
// cast failed, return default(T) or throw
}
}
}
}
그러나이 경우 단순히 데이터를 객체로 전달하고 직접 캐스트하지 않는 이유는 무엇입니까?
필요에 따라 "C#"에서 "PHP"를 시도 할 수도 있습니다.
namespace Project1 {
public class Class1 {
static int Main(string[] args) {
MyInt a = 1;
MyInt b = "2";
Console.WriteLine(a + b); // writes 3
Console.ReadLine();
return 0;
}
}
class MyInt {
private int value;
public static implicit operator int(MyInt container) {
return container.value;
}
public static implicit operator MyInt(int value) {
MyInt myInt = new MyInt();
myInt.value = value;
return myInt ;
}
public static implicit operator MyInt(string stringedInt) {
MyInt myInt = new MyInt();
myInt.value = int.Parse(stringedInt);
return myInt;
}
}
}
죄송합니다. 전제를 사지 않습니다. 데이터가 모두 같은 목적을 갖는 경우 모두 동일한 유형을 가져야합니다. 여러 웹 서비스 중 하나에 의해 반환 된대로 현재 온도를 유지하기위한 클래스를 고려하십시오. 모든 서비스는 온도를 Centighade에서 반환합니다. 그러나 하나는 int로 돌아오고, 하나는 더블로 돌아오고, 하나는 그것을 문자열로 반환합니다.
세 가지 다른 유형이 아닙니다. 하나의 유형입니다 - 더블. 비-가우블 리턴을 두 배로 변환하면 온도가있는 것 (또는 아마도 플로트)입니다.
일반적으로 한 가지를 여러 가지 표현을 가지고 있다면 여전히 여러 가지가 아니라 한 가지입니다. 다중 표현을 하나로 변환하십시오.