스마트엠: 조인 스키트가 뭘 잘못했어.
17480 단어 dotnetcsharpcomputerscience
2004년 조인 스키트(Jon Skeet)가 발표한 글Violating the "Smart Enum" Pattern in C#은 매우 위험한 문제를 묘사했다.그리고 나서 그는 문제를 패턴 탓으로 돌렸다.
이건 패턴이 아니야.여러 해 동안 존스윗, 에릭 라이퍼트, 스티브 스미스 등 똑똑한 C#dev는 이 개념의 에버라이빙을 과도하게 디자인해 모델 때문에 생긴 문제라고 주장했다.이것은'매거+데이터'가 아니라 그들이 그것을 어떻게 실현하는가이다.
그러니 다른 일을 하기 전에 목적이 무엇인지 이야기해 봅시다.Java에서 매거는 상당히 뚱뚱한 대상이며, 그 영감은 Java에서 나온다.물론 다른 언어의 매거진처럼 사용할 수 있지만, 추가 데이터를 휴대할 수도 있다.다음은 직접 추출Oracle의 예입니다.
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java Planet <earth_weight>");
System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}
물론 C#enum 으로는 불가능합니다!지금은 앞서 언급한 개인과 다른 사람들이 시도하는 것보다 C#에서 이를 실현하는 방법이 훨씬 낫다.단지 다른 언어가 어떤 것을 실현시켰기 때문에 어리석은 것이다.하지만, 헤헤, 나도 터무니없는 언어 실험을 많이 했기 때문에, 나는 알았다.
그의 블로그 글에서 그가 묘사한 모델의 세부 사항은 유사한 결과를 초래할 수 있다(나는 편집에서 이런 것을 쓴 적이 없기 때문에 잘못에 대해 사과한다).
public class Planet {
public static readonly Planet MERCURY = new Mercury();
public static readonly Planet VENUS = new Venus();
public static readonly Planet EARTH = new Earth();
public static readonly Planet MARS = new Mars();
public static readonly Planet JUPITER = new Jupiter();
public static readonly Planet SATURN = new Saturn();
public static readonly Planet URANUS = new Uranus();
public static readonly Planet NEPTUNE = new Neptune();
private readonly double mass; // in kilograms
private readonly double radius; // in meters
private Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
// Implement the other stuff here...
// Now create the "enumerations"
private class Mercury {
private Mercury() : base(3.303e+23, 2.4397e+6) { }
}
private class Venus {
private Venus() : base(4.869e+24, 6.0518e+6) { }
}
// And so on...
}
보다지루하다, 그렇지?그럼요.또한, 완전한 매거 지원을 원한다면, 세 개의 인터페이스를 추가해야 한다. 그 중 하나는... 이다.실시하기에 결코 유쾌하지 않다.실제로, 이것은 지원하는 정수치와 이름의 문자열 등 다른 정보를 포함할 수 있음을 의미합니다. (반사를 사용하려고 하지 않으면, 이름의 속도가 매우 느립니다.)이 모든 것은 팽창을 의미한다.코드로 팽창할 뿐만 아니라 대상까지 팽창한다.그러나 Jon Skeet이 묘사한 이러한 실현 모델의 방법에서 John Payson은 문제를 발견했다.귀속 구조 함수 체인을 만드는 메커니즘은 이미 불가능하다. (또는, 적어도 오류로 정확하게 표시되어 있으며, 나는 아직 그것을 억제하려고 시도하지 않았다.)귀속 구조 함수 체인과 종결기 부활 반모드를 교묘하게 사용하면 숨겨진 구조 함수가 존재하지만 대상을 되찾을 수 있습니다.물론 서열화와 반사에도 기교가 있다.
문제는 이 중 대부분이 필요 없다는 것이다!
우리가 뭘 해야 할지 생각해봐.추가 데이터와 가능한 방법이 있는 일일이 열거나는 방법 부분에 관심이 없다. 왜냐하면 확장 방법은 이미 존재하기 때문이다."열거 + 데이터"섹션을 살펴보겠습니다.
좋아, 우리 글로 이 점을 이해하자.
public sealed class Planet {
public static readonly Planet MERCURY = new Planet(Base.Mercury, 3.303e+23, 2.4397e6);
public static readonly Planet VENUS = new Planet(Base.Venus, 4.869e+24, 6.0518e6);
// And so on...
private readonly Base @base;
private readonly double mass; // in kilograms
private readonly double radius; // in meters
private Planet(Base @base, double mass, double radius) {
this.@base = @base;
this.mass = mass;
this.radius = radius;
}
private enum Base {
Mercury,
Venus,
// And so on...
}
// Implement the other stuff here ...
}
비록 공공 API는 보기에는 같지만, 이것은 우리에게 유용한 물건을 가져다 주었다.우선, 모든 Enum API는 여전히 작동하도록 쉽게 조정할 수 있습니다. 왜냐하면...그것은 여전히 일거수일투족이다.Enum이 구현한 모든 인터페이스를 쉽게 지원할 수 있으며, VisualStudio는 @base
를 통해 전체 인터페이스를 생성할 수 있습니다!그러나 이 유형은 현재sealed
도 가능하기 때문에 조인 스키트(Jon Skeet)가 보여준 기교를 시도할 수 없다.여기에 반사하는 것도 매우 유용하지 않습니다. 접근 구조 함수는 특별한 것을 만들 수 없기 때문입니다.등식과 비교는 여전히 @base
를 통해 정의된 것이기 때문에 같은 기초값을 가진 새로운 실례는 여전히 같은 방식으로 계산되고 본질적으로 값의 의미를 가진다.서열화 프로그램을 남용하여 새 대상을 만듭니까?네, 받으실 거예요. default(Base)
이 예에서 딱 맞아요. Base.Mercury
너는 항상 효과적인 상대가 있다.물론, 나는 여전히 사기를 통해 무효한 대상을 만들 수 있는 방법이 있다.그것은 확실히 반성과 관련이 있지만, 다른 것들도 관련이 있다.반사로 구조 함수를 얻은 다음 무효한 매거진값을
Base
로 강제로 변환하여 구조 함수에 전달합니다.그러나 나는 심지어 이것이 나의 디자인 방법의 오류라고 생각하지 않는다. 왜냐하면 이것은 C#가 사용하는 모든 정수가 지원하는 매거진과 거의 모든 단일 매거진이 실현되는 이미 알고 있는 문제이기 때문이다.나의 프로그래머 동료들에게 이 글을 경고로 삼는 것에 주의하십시오.총명함은 왕왕 너에게 폐를 끼친다.더 이상 똑똑하지 마라.코드가 당신보다 똑똑해지면 오해하기 때문이다.
하지만 정말 이런 패턴을 사용하지 마세요.네가 필요로 할 수 있는 모든 것을 실현할 수 있는 더 좋은 방법이 있다.
Reference
이 문제에 관하여(스마트엠: 조인 스키트가 뭘 잘못했어.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/entomy/smart-enum-what-jon-skeet-got-wrong-16m4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)