Pregunta

¿Qué es un buen diseño en este caso sencillo:

Vamos a decir que tengo un coche de la clase base con un método en el que FillTank(Fuel fuel) combustible es también una clase base que tienen varias clases de hojas, diesel, etanol etc.

En mi hoja categoría de coches DieselCar.FillTank(Fuel fuel) sólo un determinado tipo de combustible se permite (no hay sorpresas :)). Ahora aquí es mi preocupación, de acuerdo a mi interfaz de cada coche puede ser tankeado con cualquier combustible, pero que parece mal a mí, en cada aplicación FillTank() comprobar el combustible de entrada para el tipo correcto y si no tira de error o algo así.

¿Cómo puedo rediseñar este caso a una más precisa, ¿es posible? Cómo diseñar un método de base que tiene una clase base para la entrada sin conseguir estos resultados "extraños"?

¿Fue útil?

Solución

Si hay un límite bien definido entre los tipos de vehículos y tipos de combustible, a continuación, FillTank() tiene nada que hacer en la clase Car base, ya que al conocer que usted tiene un coche no se qué tipo de combustible no contar. Así, por esto para garantizar la corrección en el tiempo de compilación, FillTank() deberían definirse en las subclases, y sólo debe tomar la subclase Fuel que funciona.

Pero lo que si usted tiene código común que no desea repetir entre las subclases? A continuación, se escribe un método protegido FillingTank() para la clase base que las llamadas de función de la subclase. Lo mismo ocurre con Fuel.

Pero lo que si usted tiene algún coche mágico que se ejecuta en múltiples combustibles, por ejemplo gasóleo o gas? Luego que el coche se convierte en una subclase de ambos DieselCar y GasCar y que necesita para asegurarse de que Car está declarada como una superclase virtual, de modo que usted no tiene dos instancias Car en un objeto DualFuelCar. Llenar el tanque Sólo debe trabajar con poca o ninguna modificación:. Por defecto, obtendrá tanto DualFuelCar.FillTank(GasFuel) y DualFuelCar.FillTank(DieselFuel), que le da una función sobrecargada por tipo

Pero lo que si usted no quiere que la subclase de tener una función FillTank()? Luego hay que cambiar a tiempo de ejecución de cheques y hacer lo que creía que había que: hacer que el Fuel.type cheque subclase y, o bien una excepción o devuelve un código de error (se prefiere este último) si hay una discrepancia . En C ++, RTTI y dynamic_cast<> son lo que yo recomendaría. En Python, isinstance().

Otros consejos

Utilice una clase base genérica (si su idioma soporta (el siguiente es C #)):

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

Básicamente esto hace cumplir cualquier clase que hereda de coche para especificar qué tipo de combustible que utiliza. Además, la clase Car impone una restricción que TFuel debe ser algún subtipo de la clase Fuel abstracto.

digamos que tenemos un poco de Diesel clase que es simple:

public class Diesel : Fuel
{
    ...
}

Y un coche que sólo funciona con diesel:

public DieselCar : Car<Diesel>
{
     public override void FillTank(Diesel fuel)
     {
          //perform diesel fuel logic here.
     }
}

programación por sí sola no puede manejar bien este problema orientado a objetos. Lo que necesita es la programación genérica (solución C ++ se muestra aquí):

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

Su coche diesel es entonces sólo un coche específico, Car<Diesel>.

a doble despacho se puede utilizar para esto: aceptar algo de combustible antes de antes del llenado. Ten en cuenta que en un lenguaje que no apoyan directamente, se introduce dependencias

Parece que lo que desea es restringir el tipo de combustible que entra en su coche diesel. Algo así como:

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>
{
}

Lo haría por ejemplo el truco.

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

En esencia, lo que está haciendo aquí está permitiendo un Car tener tipos de combustibles específicos. También le permite crear un coche que apoyaría cualquier tipo de Fuel (la oportunidad sería una buena cosa!). Sin embargo, en su caso, el DieselCar, usted acaba de derivar una clase de coche y restringirlo a usar solamente combustible Diesel.

utilizar el operador is para comprobar contra las clases aceptados, y se puede lanzar una excepción en el constructor

Creo que el método aceptado sería tener un método ValidFuel(Fuel f) en su clase base que lanza una especie de NotImplementedException (diferentes idiomas tienen diferentes términos) si los coches "hoja" no anulan la misma.

FillTank podría entonces ser del todo en la clase base y llamar ValidFuel para ver si es válida.

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
    }
}

En un sistema CLOS similar, se podría hacer algo como esto:

(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"))

que le da este comportamiento:

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"

Lo que, en realidad, es la versión de Common Lisp del doble de despacho, como se ha mencionado por stefaanv .

puede extender su interfaz para el automóvil original

interface Car {
    drive();
}

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

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

}

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top