정적 메서드 상속에 대한 올바른 대안은 무엇입니까?
정적 메서드 상속이 C #에서 지원되지 않음을 이해합니다. 또한 개발자가이 기능이 필요하다고 주장하는 많은 토론 (여기 포함)을 읽었으며, 일반적인 응답은 "정적 멤버 상속이 필요한 경우 디자인에 결함이 있습니다"입니다.
좋아, OOP가 내가 정적 상속에 대해 생각하는 것을 원하지 않는다는 점을 감안할 때, 나는 그것이 나의 명백한 필요성이 내 디자인의 오류를 가리킨다는 결론을 내려야한다. 그러나 나는 붙어 있습니다. 이 문제를 해결하는 데 도움을 주시면 감사하겠습니다. 여기에 도전이 있습니다 ...
복잡한 초기화 코드를 캡슐화하는 추상 기본 클래스 (Fruit이라고합시다)를 만들고 싶습니다. 이 코드 중 일부는 가상 메서드 호출에 의존하기 때문에 생성자에 배치 할 수 없습니다.
Fruit은 다른 구체적인 클래스 (Apple, Orange)에 상속되며, 각 클래스는 인스턴스를 만들고 초기화하기 위해 표준 팩토리 메서드 CreateInstance ()를 노출해야합니다.
정적 멤버 상속이 가능하면 팩토리 메서드를 기본 클래스에 배치하고 파생 클래스에 대한 가상 메서드 호출을 사용하여 구체적인 인스턴스를 초기화해야하는 형식을 가져옵니다. 클라이언트 코드는 Apple.CreateInstance ()를 간단히 호출하여 완전히 초기화 된 Apple 인스턴스를 얻습니다.
그러나 이것은 분명히 불가능하므로 누군가 동일한 기능을 수용하기 위해 내 디자인을 변경해야하는 방법을 설명해 주시겠습니까?
하나의 아이디어 :
public abstract class Fruit<T>
where T : Fruit<T>, new()
{
public static T CreateInstance()
{
T newFruit = new T();
newFruit.Initialize(); // Calls Apple.Initialize
return newFruit;
}
protected abstract void Initialize();
}
public class Apple : Fruit<Apple>
{
protected override void Initialize() { ... }
}
다음과 같이 전화하십시오.
Apple myAppleVar = Fruit<Apple>.CreateInstance();
추가 공장 수업이 필요하지 않습니다.
팩토리 메서드를 형식 밖으로 이동하고 자체 Factory 클래스에 넣습니다.
public abstract class Fruit
{
protected Fruit() {}
public abstract string Define();
}
public class Apple : Fruit
{
public Apple() {}
public override string Define()
{
return "Apple";
}
}
public class Orange : Fruit
{
public Orange() {}
public override string Define()
{
return "Orange";
}
}
public static class FruitFactory<T>
{
public static T CreateFruit<T>() where T : Fruit, new()
{
return new T();
}
}
그러나 내가보고있는 것처럼 Create 메서드를 자체 Factory 클래스로 옮길 필요가 없습니다 (비록 우려 사항 분리가 더 좋다고 생각하지만), Fruit 클래스에 넣을 수 있습니다.
public abstract class Fruit
{
public abstract string Define();
public static T CreateFruit<T>() where T : Fruit, new()
{
return new T();
}
}
그리고 작동하는지 확인하려면 :
class Program
{
static void Main( string[] args )
{
Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ());
Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ());
Console.ReadLine ();
}
}
create 메서드로 팩토리 클래스 (템플릿)를 생성하지 않는 이유는 무엇입니까?
FruitFactory<Banana>.Create();
나는 이렇게 할 것이다
public abstract class Fruit() {
public abstract void Initialize();
}
public class Apple() : Fruit {
public override void Initialize() {
}
}
public class FruitFactory<T> where T : Fruit, new {
public static <T> CreateInstance<T>() {
T fruit = new T();
fruit.Initialize();
return fruit;
}
}
var fruit = FruitFactory<Apple>.CreateInstance()
WebRequest
.NET BCL 의 클래스 및 파생 유형은 이러한 종류의 디자인을 비교적 잘 구현할 수있는 방법에 대한 좋은 예를 나타냅니다.
WebRequest
클래스는 등 여러 가지 하위 클래스를 가지고 HttpWebRequest
와 FtpWebReuest
. 이제이 WebRequest
기본 클래스도 팩토리 유형이며 정적 Create
메서드를 노출합니다 (팩토리 패턴에서 요구하는대로 인스턴스 생성자는 숨겨 짐).
public static WebRequest Create(string requestUriString)
public static WebRequest Create(Uri requestUri)
이 Create
메서드는 WebRequest
클래스 의 특정 구현을 반환 하고 URI (또는 URI 문자열)를 사용하여 만들고 반환 할 개체의 유형을 결정합니다.
이것은 다음 사용 패턴의 최종 결과를 갖습니다.
var httpRequest = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com/");
// or equivalently
var httpRequest = (HttpWebRequest)HttpWebWebRequest.Create("http://stackoverflow.com/");
var ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://stackoverflow.com/");
// or equivalently
var ftpRequest = (FtpWebRequest)FtpWebWebRequest.Create("ftp://stackoverflow.com/");
개인적으로 이것이 문제에 접근하는 좋은 방법이라고 생각하며 실제로 .NET Framework 제작자가 선호하는 방법 인 것 같습니다.
First of all, not having static initializers that can be virtual doesn't mean you can't have "standard" member methods, that could be overloaded. Second of all, you can call your virtual methods from constructors, and they will work as expected, so there's no problem here. Third of all, You can use generics to have type-safe factory.
Here's some code, that uses factory + member Initialize() method that is called by constructor (and it's protected, so you don't have to worry, that someone will call it again after creating an object):
abstract class Fruit
{
public Fruit()
{
Initialize();
}
protected virtual void Initialize()
{
Console.WriteLine("Fruit.Initialize");
}
}
class Apple : Fruit
{
public Apple()
: base()
{ }
protected override void Initialize()
{
base.Initialize();
Console.WriteLine("Apple.Initialize");
}
public override string ToString()
{
return "Apple";
}
}
class Orange : Fruit
{
public Orange()
: base()
{ }
protected override void Initialize()
{
base.Initialize();
Console.WriteLine("Orange.Initialize");
}
public override string ToString()
{
return "Orange";
}
}
class FruitFactory
{
public static T CreateFruit<T>() where T : Fruit, new()
{
return new T();
}
}
public class Program
{
static void Main()
{
Apple apple = FruitFactory.CreateFruit<Apple>();
Console.WriteLine(apple.ToString());
Orange orange = new Orange();
Console.WriteLine(orange.ToString());
Fruit appleFruit = FruitFactory.CreateFruit<Apple>();
Console.WriteLine(appleFruit.ToString());
}
}
I'd say the best thing to do is to create a virtual/abstract Initialise method on the fruit class which must be called and then create an external 'fruit factory' class to create instances:
public class Fruit
{
//other members...
public abstract void Initialise();
}
public class FruitFactory()
{
public Fruit CreateInstance()
{
Fruit f = //decide which fruit to create
f.Initialise();
return f;
}
}
'IT TIP' 카테고리의 다른 글
명령 줄 HTML 프리티 프린터 : 복잡한 HTML을 읽기 쉽게 만들기 (0) | 2020.10.12 |
---|---|
Windows 용 Systrace (0) | 2020.10.12 |
Java BigDecimal 가능한 오버 플로우 버그 (0) | 2020.10.12 |
Dagger- 각 활동 / 조각에 대해 각 구성 요소와 모듈을 만들어야합니까? (0) | 2020.10.12 |
list ()는 list comprehension보다 약간 더 많은 메모리를 사용합니다. (0) | 2020.10.12 |