2011-01-04 21 views
35

Soy un novato e intento comprender los conceptos de herencia y patrones de diseño.Pasando Clase derivada como parámetro de un método cuando el tipo de parámetro es clase base

Me encontré con este patrón http://en.wikipedia.org/wiki/Strategy_pattern cuando estaba revisando algún blog.

Me pareció interesante y quería aprender más. Entonces desarrollé el siguiente programa.

static void Main(string[] args) 
{ 
    Context context; 

    // Three contexts following different strategies 
    context = new Context(new ConcreteStrategyAdd()); 
    int resultA = context.executeStrategy(3, 4); 

    context = new Context(new ConcreteStrategySubtract()); 
    int resultB = context.executeStrategy(3, 4); 

    context = new Context(new ConcreteStrategyMultiply()); 
    int resultC = context.executeStrategy(3, 4); 

    Console.Read(); 
}  

abstract class Strategy 
{ 
    public abstract int execute(int a, int b); 

    public void Test() 
    { 
     Console.Write("tttt"); 
    } 
} 

class ConcreteStrategyAdd : Strategy 
{ 
    public override int execute(int a, int b) 
    { 
     Console.WriteLine("Called ConcreteStrategyAdd's execute()"); 
     return a + b; // Do an addition with a and b 
    } 
} 

class ConcreteStrategySubtract : Strategy 
{ 

    public override int execute(int a, int b) 
    { 
     Console.WriteLine("Called ConcreteStrategySubtract's execute()"); 
     return a - b; // Do a subtraction with a and b 
    } 
} 

class ConcreteStrategyMultiply : Strategy 
{ 
    public override int execute(int a, int b) 
    { 
     Console.WriteLine("Called ConcreteStrategyMultiply's execute()"); 
     return a * b; // Do a multiplication with a and b 
    } 
} 

class Context 
{ 
    private Strategy strategy; 

    // Constructor 
    public Context(Strategy strategy) 
    { 
     this.strategy = strategy; 
    } 

    public int executeStrategy(int a, int b) 
    { 
     return strategy.execute(a, b); 
    } 
} 

El programa compila bien y está funcionando. Pero mi pregunta es ¿cómo es posible pasar la clase derivada como parámetro cuando el constructor Context espera una clase base como parámetro? ¿El casting está sucediendo implícitamente? ¿Por qué el compilador no tiene errores?

context = new Context(new ConcreteStrategyAdd()); 

public Context(Strategy strategy) 
{ 
    this.strategy = strategy; 
} 
+7

+1 para una buena pregunta principiante! –

+1

Para un novato, usted está haciendo todas las preguntas correctas :) +1 – MattDavey

Respuesta

25

poner realmente simplemente:

una clase derivada (o subclase) es una instancia de su clase base.

Por lo tanto, cuando se pasa una instancia de ConcreteStrategyAdd en el constructor, está de paso esencialmente un objeto en Strategy.

No hay fundición involucrados. La jerarquía de tipos permite este tipo de programación. Permite a los programadores usar polymorphism en su código.

+0

Me gustaría pasar un Tipo de clase como parámetro (no su instancia) y todavía limitar el valor al de una clase base de jerarquía de clases. ¿Es posible? para lograr el tiempo de compilación verificando esto? por ejemplo, Java permite la clase como parámetro (como su clase Class es genérica), desafortunadamente el tipo .net no es genérico: -/ Oh, debo mencionar, estoy tratando de hacer esto para un constructor - entonces los genéricos no son permisible –

2

La clase derivada es una clase base. (Ese es todo el concepto de herencia: la herencia define una relación 'es una').

Además, consulte Liskov substitution principle. :)

+1

ese enlace me hizo bastante confundido, en lugar de ayudar. – BerggreenDK

+0

LSP es ciertamente pertinente para este tema, pero tal vez un poco demasiado avanzado para una pregunta para principiantes :) – MattDavey

2

es más fácil de entender si utiliza un ejemplo más simplista que el patrón de estrategia.

supongamos que tiene una clase llamada "Fruta" y una clase llamada "Manzana" que se deriva de la fruta. cualquier método que se escribe para trabajar con la fruta en general, puede funcionar bien con un Apple o cualquier otro tipo específico de fruta

24

No hay colada sea necesario, ya que ConcreteStrategyAdd es una Estrategia - que satisface todos los requisitos de ser una estrategia. Este es el principio del polimorfismo.

Tal vez se necesita un ejemplo más simple:

abstract class Fruit { } 

class Apple : Fruit { } 
class Orange : Fruit { } 
class Melon : Fruit { } 

class FruitBasket 
{ 
    void Add(Fruit item) { ... } 
} 

FruitBasket basket = new FruitBasket(); 
basket.Add(new Apple()); // Apple IS A fruit 
basket.Add(new Orange()); // Orange IS A fruit 
basket.Add(new Melon()); // Melon IS A fruit 

class Potato : Vegetable { } 

basket.Add(new Potato()); // ERROR! Potato IS NOT A fruit. 
+2

Me gusta el "ejemplo de la vida real". Buena personaLos autos y las frutas son excelentes ejemplos. – BerggreenDK

+0

Ese fue un buen ejemplo, y probablemente más claro para algunos que los ejemplos regulares de B: A y autos. –

Cuestiones relacionadas