2008-12-03 11 views
17

Supongamos que tengo una clase 'Aplicación'. Para inicializarse, requiere ciertas configuraciones en el constructor. Supongamos también que la cantidad de configuraciones es tan alta que resulta convincente ubicarlas en una clase propia.Clases anidadas "públicas" o no

Compare las siguientes dos implementaciones de este escenario.

Implementación 1:

class Application 
{ 
    Application(ApplicationSettings settings) 
    { 
     //Do initialisation here 
    } 
} 

class ApplicationSettings 
{ 
    //Settings related methods and properties here 
} 

Aplicación 2:

class Application 
{ 
    Application(Application.Settings settings) 
    { 
     //Do initialisation here 
    } 

    class Settings 
    { 
     //Settings related methods and properties here 
    } 
} 

Para mí, el segundo enfoque es mucho más preferible. Es más legible porque enfatiza fuertemente la relación entre las dos clases. Cuando escribo código para instanciar la clase Application en cualquier lugar, el segundo enfoque se verá más bonito.

Ahora imagina que la clase de Configuración a su vez tenía una clase similar "relacionada" y esa clase a su vez también lo hizo. Vaya solo a tres niveles y el nombre de la clase se salga de control en el caso "no anidado". Si anida, sin embargo, las cosas siguen siendo elegantes.

A pesar de lo anterior, he leído personas que decían en StackOverflow que las clases anidadas se justifican solo si no son visibles para el mundo exterior; eso es si se usan solo para la implementación interna de la clase contenedora. La objeción comúnmente citada es hinchar el tamaño del archivo fuente de la clase que contiene, pero las clases parciales son la solución perfecta para ese problema.

Mi pregunta es, ¿por qué tenemos cuidado con el uso "públicamente expuesto" de las clases anidadas? ¿Hay algún otro argumento en contra de tal uso?

Respuesta

22

Creo que está bien. Este es básicamente el patrón del constructor, y el uso de clases anidadas funciona bastante bien. También permite que el constructor acceda a miembros privados de la clase externa, lo que puede ser muy útil. Por ejemplo, puede tener un método Construir sobre el constructor que llama a un constructor privado en la clase externa que tiene una instancia del constructor:

public class Outer 
{ 
    private Outer(Builder builder) 
    { 
     // Copy stuff 
    } 

    public class Builder 
    { 
     public Outer Build() 
     { 
      return new Outer(this); 
     } 
    } 
} 

Eso asegura que el única manera de construir una instancia de la la clase externa es a través del constructor.

Uso un patrón muy parecido a este en mi puerto C# de Protocol Buffers.

+0

Tuve que escribir un código de generador recientemente y recordé esta publicación. Resultó ser un enfoque muy útil, así que gracias. –

+0

* "También permite que el constructor acceda a miembros privados de la clase externa" * Solo con una referencia explícita, correcta (estoy bastante seguro de que no está implícita como en las clases internas de Java) ?. – samosaris

+0

@SamusArin: Sí, no hay referencia implícita a una instancia de la clase contenedora. Pero también tienes acceso a miembros estáticos. Básicamente, estaba * solo * hablando de accesibilidad. –

0

Es posible que desee comprobar lo que Microsoft has to say sobre el tema. Básicamente es una cuestión de estilo, diría yo.

+0

.NET no impone la regla de "una clase (pública) por archivo" que tiene Java. Parecen tener diferentes paradigmas para la estructura del programa. WRT esta sola regla, .NET tiene más flexibilidad (un súper conjunto expresivo si se quiere). Gran artículo por cierto. – samosaris

0

Principalmente uso clases anidadas para ajustar el acceso a la clase anidada y/o contenedor.

Una cosa para recordar es que una definición de clase anidada es básicamente un miembro de clase, y tendrá acceso a todas las variables privadas del contenedor.

También puede usar esto para controlar el uso de una clase específica.

Ejemplo:

public abstract class Outer 
{ 
    protected class Inner 
    { 
    } 
} 

Ahora, en este caso, el usuario (de la clase) sólo se puede acceder a la clase interna, si se implementa exterior.

+1

Iff puede no ser un error tipográfico, pero * es * algo redundante ya que ya tiene la palabra "solo" en la oración. – phoog

5

Puede usar espacios de nombres para relacionar cosas que están ... relacionadas.

Por ejemplo:

namespace Diner 
{ 
    public class Sandwich 
    { 
     public Sandwich(Filling filling) { } 
    } 

    public class Filling { } 
} 

La ventaja de este sobre el uso de clases como si fueran espacios de nombres es que se puede usar opcionalmente using en el lado llamante para abreviar cosas:

using Diner; 

... 

var sandwich = new Sandwich(new Filling()); 

Si use la clase Sandwich como si fuera un espacio de nombre para Filling, debe usar el nombre completo Sandwich.Filling para referirse a Filling.

¿Y cómo vas a dormir en la noche sabiendo eso?

+2

Estoy de acuerdo con la preferencia por los espacios de nombres, pero a veces no funcionan del todo. En el ejemplo en cuestión, por ejemplo, semánticamente 'Aplicación' subsume 'Configuración', pero poner ambos en un espacio de nombre común no transmitiría ese significado. –

+0

** 'utilizando Filling = Diner.Filling;' ** debería resolver "referencias" a (solo) ** 'Filling' ** en el caso de" clases como espacio de nombres ". – samosaris

0

Otro ejemplo práctico que tengo para un uso válido de las clases anidadas públicas está en el patrón MVC cuando uso un modelo de vista con una propiedad IEnumerable. por ejemplo:

public class OrderViewModel 
{ 
public int OrderId{ get; set; } 
public IEnumerable<Product> Products{ get; set; } 

public class Product { 
public string ProductName{ get; set; } 
public decimal ProductPrice{ get; set; } 
} 

} 

lo uso porque no quiero Product clase para ser reutilizado fuera porque está modificada a medida sólo para ese modelo de vista específico que lo contiene. Pero no puedo hacerlo privado porque la propiedad de los Productos es pública.

Cuestiones relacionadas