una tipos tener puede polimorfismo ejercicios ejemplos cuantos constructores clase java oop scala immutability

tipos - polimorfismo java



Construir objetos grandes e inmutables sin usar constructores con largas listas de parĂ¡metros (9)

Aquí hay un par de opciones más:

Opción 1

Haga la implementación en sí misma mutable, pero separe las interfaces que expone a mutables e inmutables. Esto se toma del diseño de la biblioteca Swing.

public interface Foo { X getX(); Y getY(); } public interface MutableFoo extends Foo { void setX(X x); void setY(Y y); } public class FooImpl implements MutableFoo {...} public SomeClassThatUsesFoo { public Foo makeFoo(...) { MutableFoo ret = new MutableFoo... ret.setX(...); ret.setY(...); return ret; // As Foo, not MutableFoo } }

opcion 2

Si su aplicación contiene un conjunto grande pero predefinido de objetos inmutables (por ejemplo, objetos de configuración), puede considerar usar el marco Spring .

Tengo algunos objetos grandes (más de 3 campos) que pueden y deben ser inmutables. Cada vez que me encuentro con ese caso, tiendo a crear abominaciones de constructores con largas listas de parámetros. No se siente bien, es difícil de usar y la legibilidad sufre.

Es aún peor si los campos son algún tipo de colección, como listas. Un simple addSibling(S s) facilitaría mucho la creación del objeto, pero lo haría mutable.

¿Qué usan ustedes en estos casos? Estoy en Scala y Java, pero creo que el problema es independiente del lenguaje siempre que el lenguaje esté orientado a objetos.

Soluciones en las que puedo pensar:

  1. "Abominaciones de constructores con largas listas de parámetros"
  2. El patrón de construcción

¡Gracias por tu contribución!


Ayuda a recordar que hay diferentes tipos de inmutabilidad . Para su caso, creo que la inmutabilidad "popsicle" funcionará muy bien:

Inmutabilidad de paleta: es lo que llamo caprichosamente un ligero debilitamiento de la inmutabilidad de escritura única. Uno podría imaginar un objeto o un campo que permaneció mutable durante un tiempo durante su inicialización, y luego se "congelaron" para siempre. Este tipo de inmutabilidad es particularmente útil para objetos inmutables que se referencian circularmente entre sí, o para objetos inmutables que han sido serializados en disco y luego de la deserialización deben ser "fluidos" hasta que se complete todo el proceso de deserialización, momento en el que todos los objetos pueden ser congelado.

Entonces inicializas tu objeto, luego configuras un indicador de "congelación" de algún tipo que indica que ya no se puede escribir. Preferiblemente, ocultaría la mutación detrás de una función, por lo que la función aún es pura para los clientes que consumen su API.


Bueno, ¿quieres un objeto más fácil de leer e inmutable una vez creado?

Creo que una interfaz fluida CORRECTAMENTE HECHA te ayudaría.

Se vería así (ejemplo puramente inventado):

final Foo immutable = FooFactory.create() .whereRangeConstraintsAre(100,300) .withColor(Color.BLUE) .withArea(234) .withInterspacing(12) .build();

Escribí "CORRECTAMENTE HECHO" en negrita porque la mayoría de los programadores de Java tienen mal las interfaces fluidas y contaminan su objeto con el método necesario para construir el objeto, lo cual es completamente erróneo.

El truco es que solo el método build () realmente crea un Foo (por lo tanto, Foo puede ser inmutable).

FooFactory.create () , dondeXXX (..) y withXXX (..) crean todos "algo más".

Ese algo más puede ser un FooFactory, aquí hay una manera de hacerlo ...

You FooFactory se vería así:

// Notice the private FooFactory constructor private FooFactory() { } public static FooFactory create() { return new FooFactory(); } public FooFactory withColor( final Color col ) { this.color = color; return this; } public Foo build() { return new FooImpl( color, and, all, the, other, parameters, go, here ); }


Bueno, considera esto en Scala 2.8:

case class Person(name: String, married: Boolean = false, espouse: Option[String] = None, children: Set[String] = Set.empty) { def marriedTo(whom: String) = this.copy(married = true, espouse = Some(whom)) def addChild(whom: String) = this.copy(children = children + whom) } scala> Person("Joseph").marriedTo("Mary").addChild("Jesus") res1: Person = Person(Joseph,true,Some(Mary),Set(Jesus))

Esto tiene su parte de problemas, por supuesto. Por ejemplo, intente hacer espouse y Option[Person] , y luego obtener dos personas casadas entre sí. No puedo pensar en una forma de resolver eso sin recurrir a una private var o un constructor private más una fábrica.


Considera cuatro posibilidades:

new Immutable(one, fish, two, fish, red, fish, blue, fish); /*1 */ params = new ImmutableParameters(); /*2 */ params.setType("fowl"); new Immutable(params); factory = new ImmutableFactory(); /*3 */ factory.setType("fish"); factory.getInstance(); Immutable boringImmutable = new Immutable(); /* 4 */ Immutable lessBoring = boringImmutable.setType("vegetable");

Para mí, cada uno de 2, 3 y 4 está adaptado a una situación de diferencia. El primero es difícil de amar, por las razones citadas por el OP, y en general es un síntoma de un diseño que ha sufrido algunos problemas y necesita algunas refactorizaciones.

Lo que estoy enumerando como (2) es bueno cuando no hay un estado detrás de la ''fábrica'', mientras que (3) es el diseño de elección cuando hay estado. Me encuentro utilizando (2) en lugar de (3) cuando no quiero preocuparme por los hilos y la sincronización, y no tengo que preocuparme por amortizar alguna costosa configuración sobre la producción de muchos objetos. (3), por otro lado, se invoca cuando el trabajo real entra en la construcción de la fábrica (configuración desde un SPI, lectura de archivos de configuración, etc.).

Finalmente, la respuesta de otra persona mencionó la opción (4), donde tienes muchos objetos pequeños inmutables y el patrón preferible es obtener noticias de los viejos.

Tenga en cuenta que no soy miembro del ''club de fans del patrón'', seguro, vale la pena emular algunas cosas, pero me parece que adquieren una vida inútil una vez que la gente les da nombres y sombreros divertidos.


En Scala 2.8, puede usar parámetros con nombre y predeterminados, así como el método de copy en una clase de caso. Aquí hay un código de ejemplo:

case class Person(name: String, age: Int, children: List[Person] = List()) { def addChild(p: Person) = copy(children = p :: this.children) } val parent = Person(name = "Bob", age = 55) .addChild(Person("Lisa", 23)) .addChild(Person("Peter", 16))


Otra opción posible es refactorizar para tener menos campos configurables. Si los grupos de campos solo funcionan (en su mayoría) entre sí, recógelos en su propio objeto pequeño e inmutable. Los constructores / constructores de objetos "pequeños" deberían ser más manejables, al igual que el constructor / constructor de este objeto "grande".


También podría hacer que los objetos inmutables expongan métodos que parecen mutadores (como addSibling) pero les permita devolver una nueva instancia. Eso es lo que hacen las colecciones inmutables de Scala.

La desventaja es que puede crear más instancias de las necesarias. También es aplicable solo cuando existen configuraciones intermedias válidas (como un nodo sin hermanos que está bien en la mayoría de los casos) a menos que no desee tratar con objetos parcialmente construidos.

Por ejemplo, un borde de gráfico que no tiene un destino todavía no es un borde gráfico válido.


Yo uso C #, y estos son mis enfoques. Considerar:

class Foo { // private fields only to be written inside a constructor private readonly int i; private readonly string s; private readonly Bar b; // public getter properties public int I { get { return i; } } // etc. }

Opción 1. Constructor con parámetros opcionales

public Foo(int i = 0, string s = "bla", Bar b = null) { this.i = i; this.s = s; this.b = b; }

Usado como, por ejemplo, new Foo(5, b: new Bar(whatever)) . No para versiones Java o C # anteriores a la 4.0. pero todavía vale la pena mostrarlo, ya que es un ejemplo de cómo no todas las soluciones son independientes del idioma.

Opción 2. Constructor tomando un solo objeto de parámetro

public Foo(FooParameters parameters) { this.i = parameters.I; // etc. } class FooParameters { // public properties with automatically generated private backing fields public int I { get; set; } public string S { get; set; } public Bar B { get; set; } // All properties are public, so we don''t need a full constructor. // For convenience, you could include some commonly used initialization // patterns as additional constructors. public FooParameters() { } }

Ejemplo de uso:

FooParameters fp = new FooParameters(); fp.I = 5; fp.S = "bla"; fp.B = new Bar(); Foo f = new Foo(fp);`

C # de 3.0 on hace que esto sea más elegante con la sintaxis del inicializador de objetos (semánticamente equivalente al ejemplo anterior):

FooParameters fp = new FooParameters { I = 5, S = "bla", B = new Bar() }; Foo f = new Foo(fp);

Opción 3:
Rediseñe su clase para no necesitar una cantidad tan grande de parámetros. Puede dividir sus funciones en múltiples clases. O pase los parámetros no al constructor sino a métodos específicos, a pedido. No siempre es viable, pero cuando lo es, vale la pena hacerlo.