24

この単純なケースでの優れた設計とは:

FillTank(Fuel fuel)燃料も基本クラスであり、いくつかのリーフクラス、ディーゼル、エタノールなどを持つメソッドを持つ基本クラス Car があるとします。

私のリーフカークラスDieselCar.FillTank(Fuel fuel)では、特定の種類の燃料のみが許可されています (驚くことではありません:))。私のインターフェイスによれば、すべての車に任意の燃料を入れることができますが、それは私には間違っているようFillTank()です.

そのようなケースをより正確なものに再設計するにはどうすればよいですか? これらの「奇妙な結果」を得ることなく、入力に基本クラスを取る基本メソッドを設計する方法は?

4

9 に答える 9

28

ジェネリック基本クラスを使用します (言語がサポートしている場合 (以下は C# です)):

public abstract class Car<TFuel>  where TFuel : Fuel
{
    public abstract void FillTank(TFuel fuel);
}

基本的に、これは car を継承するすべてのクラスに、使用する燃料の種類を指定するよう強制します。さらに、このクラスは、抽象クラスの何らかのサブタイプでなければならないCarという制限を課します。TFuelFuel

Diesel単純なクラスがあるとしましょう:

public class Diesel : Fuel
{
    ...
}

そして、ディーゼルのみで走る車:

public DieselCar : Car<Diesel>
{
     public override void FillTank(Diesel fuel)
     {
          //perform diesel fuel logic here.
     }
}
于 2010-02-18T14:05:02.607 に答える
12

オブジェクト指向プログラミングだけでは、この問題をうまく処理できません。必要なのはジェネリック プログラミングです (ここに示す C++ ソリューション)。

template <class FuelType>
class Car
{
public:
  void FillTank(FuelType fuel);
};

あなたのディーゼル車は、ただの特定の車ですCar<Diesel>.

于 2010-02-18T14:03:06.620 に答える
11

車の種類と燃料の種類の間に厳密な境界がある場合FillTank()、基本Carクラスにいることはできません。車を持っていることを知っていても、燃料の種類がわからないからです。したがって、これがコンパイル時に 正確であることを保証するFillTank()には、サブクラスで定義する必要があり、機能するFuelサブクラスのみを使用する必要があります。

しかし、サブクラス間で繰り返したくない共通コードがある場合はどうでしょうか? 次に、サブクラスの関数が呼び出す基本クラスの保護されたメソッドを記述します。 FillingTank()同じことが当てはまりFuelます。

しかし、ディーゼルやガソリンなど、複数の燃料で走る魔法の車があるとしたらどうでしょうか。次に、その車は両方のサブクラスになり、オブジェクトに2つのインスタンスがないように、それが仮想スーパークラスとして宣言されていることを確認する必要がありDieselCarます. タンクへの充填は、ほとんどまたはまったく変更を加えなくても機能するはずです。デフォルトでは、との両方が得られ、タイプごとにオーバーロードされた関数が得られます。GasCarCarCarDualFuelCarDualFuelCar.FillTank(GasFuel)DualFuelCar.FillTank(DieselFuel)

FillTank()しかし、サブクラスに関数を持たせたくない場合はどうでしょうか? 次に、実行時チェックに切り替えて、必要だと思っていたことを実行する必要があります。サブクラス チェックFuel.typeを行い、不一致がある場合は例外をスローするか、エラー コードを返します (後者が望ましい)。C++ では、RTTI をdynamic_cast<>お勧めします。パイソンでは、isinstance().

于 2010-02-18T14:19:21.697 に答える
1

これには、二重のディスパッチを使用できます。充填する前に、燃料を受け取ります。直接サポートしていない言語では、依存関係を導入することに注意してください

于 2010-02-18T14:05:57.077 に答える
1

ディーゼル車に入れる燃料の種類を制限したいだけのようです。何かのようなもの:

public class Fuel
{
    public Fuel()
    {
    }
}

public class Diesel: Fuel
{
}

public class Car<T> where T: Fuel
{
    public Car()
    {
    }

    public void FillTank(T fuel)
    {
    }
}

public class DieselCar: Car<Diesel>
{
}

たとえば、トリックを行います

var car = new DieselCar();
car.FillTank(/* would expect Diesel fuel only */);

基本的に、ここで行っていることは、Carに特定の燃料タイプを持たせることです。また、あらゆるタイプをサポートする車を作成することもできますFuel(可能性は十分にあります!)。ただし、DieselCar の場合、自動車からクラスを派生させ、Diesel燃料のみを使用するように制限します。

于 2010-02-18T14:14:55.047 に答える
0

オリジナルの車のインターフェースを拡張できます

interface Car {
    drive();
}

interface DieselCar extends Car {
    fillTank(Diesel fuel);
}

interface SolarCar extends Car {
    chargeBattery(Sun fuel);
}

}

于 2010-02-18T19:34:08.890 に答える
0

演算子を使用isして受け入れられたクラスをチェックし、コンストラクターで例外をスローできます

于 2010-02-18T14:00:17.710 に答える
0

「リーフ」カーがそれをオーバーライドしない場合、受け入れられる方法はValidFuel(Fuel f)、基本クラスに何らかのメソッドをスローするメソッドを持つことだと思いますNotImplementedException(言語によって用語が異なります)。

FillTankValidFuelその後、完全に基本クラスになり、それが有効かどうかを確認するために呼び出します。

public class BaseCar {
    public bool ValidFuel(Fuel f) {
        throw new Exception("IMPLEMENT THIS FUNCTION!!!");
    }

    public void FillTank(Fuel fuel) {
        if (!this.ValidFuel(fuel))
             throw new Exception("Fuel type is not valid for this car.");
        // do what you'd do to fill the car
    }
}

public class DieselCar:BaseCar {
    public bool ValidFuel(Fuel f) {
        return f is DeiselFuel
    }
}
于 2010-02-18T14:01:45.947 に答える
0

CLOS のようなシステムでは、次のようなことができます。

(defclass vehicle () ())
(defclass fuel () ())
(defgeneric fill-tank (vehicle fuel))
(defmethod fill-tank ((v vehicle) (f fuel)) (format nil "Dude, you can't put that kind of fuel in this car"))

(defclass diesel-truck (vehicle) ())
(defclass normal-truck (vehicle) ())
(defclass diesel (fuel) ())
(defmethod fill-tank ((v diesel-truck) (f diesel)) (format nil "Glug glug"))

あなたにこの振る舞いを与えます:

CL> (fill-tank (make-instance 'normal-truck) (make-instance 'diesel))
"Dude, you can't put that kind of fuel in this car"
CL> (fill-tank (make-instance 'diesel-truck) (make-instance 'diesel))
"Glug glug"

stefaanvで言及されているように、これは実際には Common Lisp のバージョンのダブル ディスパッチです。

于 2010-02-18T14:26:27.153 に答える