Dưới đây là một cách để trừu tượng ra các nhà khai thác đó là khá dễ dàng.
abstract class MathProvider<T>
{
public abstract T Divide(T a, T b);
public abstract T Multiply(T a, T b);
public abstract T Add(T a, T b);
public abstract T Negate(T a);
public virtual T Subtract(T a, T b)
{
return Add(a, Negate(b));
}
}
class DoubleMathProvider : MathProvider<double>
{
public override double Divide(double a, double b)
{
return a/b;
}
public override double Multiply(double a, double b)
{
return a * b;
}
public override double Add(double a, double b)
{
return a + b;
}
public override double Negate(double a)
{
return -a;
}
}
class IntMathProvider : MathProvider<int>
{
public override int Divide(int a, int b)
{
return a/b;
}
public override int Multiply(int a, int b)
{
return a * b;
}
public override int Add(int a, int b)
{
return a + b;
}
public override int Negate(int a)
{
return -a;
}
}
class Fraction<T>
{
static MathProvider<T> _math;
// Notice this is a type constructor. It gets run the first time a
// variable of a specific type is declared for use.
// Having _math static reduces overhead.
static Fraction()
{
// This part of the code might be cleaner by once
// using reflection and finding all the implementors of
// MathProvider and assigning the instance by the one that
// matches T.
if (typeof(T) == typeof(double))
_math = new DoubleMathProvider() as MathProvider<T>;
else if (typeof(T) == typeof(int))
_math = new IntMathProvider() as MathProvider<T>;
// ... assign other options here.
if (_math == null)
throw new InvalidOperationException(
"Type " + typeof(T).ToString() + " is not supported by Fraction.");
}
// Immutable impementations are better.
public T Numerator { get; private set; }
public T Denominator { get; private set; }
public Fraction(T numerator, T denominator)
{
// We would want this to be reduced to simpilest terms.
// For that we would need GCD, abs, and remainder operations
// defined for each math provider.
Numerator = numerator;
Denominator = denominator;
}
public static Fraction<T> operator +(Fraction<T> a, Fraction<T> b)
{
return new Fraction<T>(
_math.Add(
_math.Multiply(a.Numerator, b.Denominator),
_math.Multiply(b.Numerator, a.Denominator)),
_math.Multiply(a.Denominator, b.Denominator));
}
public static Fraction<T> operator -(Fraction<T> a, Fraction<T> b)
{
return new Fraction<T>(
_math.Subtract(
_math.Multiply(a.Numerator, b.Denominator),
_math.Multiply(b.Numerator, a.Denominator)),
_math.Multiply(a.Denominator, b.Denominator));
}
public static Fraction<T> operator /(Fraction<T> a, Fraction<T> b)
{
return new Fraction<T>(
_math.Multiply(a.Numerator, b.Denominator),
_math.Multiply(a.Denominator, b.Numerator));
}
// ... other operators would follow.
}
Nếu bạn không triển khai loại bạn sử dụng, bạn sẽ gặp lỗi khi chạy thay vì thời gian biên dịch (điều đó là xấu). Định nghĩa của việc triển khai MathProvider<T>
luôn luôn giống nhau (cũng xấu). Tôi sẽ đề nghị bạn chỉ nên tránh làm điều này trong C# và sử dụng F # hoặc một số ngôn ngữ khác phù hợp hơn với mức trừu tượng này.
Chỉnh sửa: Định nghĩa cộng và trừ cho Fraction<T>
. Một điều thú vị và đơn giản khác cần làm là triển khai MathProvider hoạt động trên một cây cú pháp trừu tượng. Ý tưởng này ngay lập tức chỉ để làm những việc như sự khác biệt tự động: http://conal.net/papers/beautiful-differentiation/
đẹp một sạch :) –
Nói chung, tôi nghĩ rằng MathProvider nên được thực hiện vào một giao diện và Trừ vào một phương pháp giao diện thông thường hoặc nó có thể được thực hiện như là một phương pháp mở rộng. Mặt khác, điều đó sẽ không thừa nhận nó. – dalle
Tôi tự hỏi về hiệu suất của giải pháp của bạn ... Chỉ hoạt động tốt nếu mọi thứ được inline ... –