Pregunta

He estado pensando en formas de proporcionar azúcar sintáctico para un marco en el que he estado trabajando. Quiero tratar exclusivamente con objetos inmutables.

Digamos que tengo un objeto inmutable y deseo crear una versión modificada del mismo. En su opinión, ¿una clase no instanciable con un único método de fábrica estático rompería los principios OO?


  

Como ejemplo usando una cadena:

public final class LOWERCASE {

    private LOWERCASE() {}

    public static String string( final String STRING ) {

      return STRING.toLowerCase();
    }
}
     

Por lo tanto, a partir de este ejemplo, podría escribir:

String lowercaseString = LOWERCASE.string( targetString );
     

Lo cual me parece muy legible.


¿Alguna condición contra tal enfoque?

¿Fue útil?

Solución

No creo que sea una buena idea crear una clase por método. En su lugar, podría crear una clase de métodos solo estáticos, llamada, por ejemplo, StringUtils e implementar los métodos. De esta manera llamarías:

String lowerCaseString = StringUtils.lowercase (targetString);

Esto también te ofrecería ayuda inteligente mientras escribes. La lista de tus clases será demasiado grande. Incluso para este simple ejemplo, debe implementar más de una clase en minúsculas, de modo que también pueda atender las circunstancias en las que se debe tener en cuenta CulutureInfo.

No creo que esto rompa los principios OO de ninguna manera o sea un mal diseño. En otros idiomas, Ruby, por ejemplo, puede agregar sus métodos directamente a la clase String. ¡Métodos que terminan con! denota que el objeto original está modificado. Todos los demás métodos devuelven una copia modificada. El marco de Ruby on Rails agrega algunos métodos a la clase String y existe cierto debate sobre si esta es una buena técnica o no. Sin embargo, definitivamente es útil.

Otros consejos

Generalmente en objetos inmutables, tendría un método que devuelve una versión modificada del objeto. Entonces, si tiene alguna colección inmutable, puede tener un método sort (), que devuelve una nueva colección que está ordenada. Sin embargo, en su ejemplo de String esto no es posible, ya que no puede tocar la clase String.

Su enfoque es bastante legible, y creo que para casos extremos como este, está perfectamente bien. Para los objetos inmutables que escriba usted mismo, tendría el método en el objeto mismo.

La serie de Eric Lippert sobre objetos inmutables en C # es bastante bueno, por cierto.

En mi opinión, el único punto de tal clase de fábrica sería si la clase proporcionara diferentes tipos de objetos inmutables.

Ej:

public final class Lowercase {

  private Lowercase() {}

  public static String string( final String string ) {

   return new String( string.toLowerCase() );
  }

  public static Foo foo( final Foo f ) {
     boolean isLowerCase = true;
     return new Foo(f, isLowerCase );
  }
}

De lo contrario, debe implementar su método en la clase inmutable como toLowerCase () en String Class.

De cualquier manera, no creo que esto rompa ningún principio OO.

Estoy de acuerdo con Erik. Los objetos inmutables siempre deben tener un método que devuelva una versión modificada del objeto en lugar de un método estático. También hay ejemplos en el JDK:

  • String.subString (int)
  • BigDecimal.movePointLeft (int)

Hacerlo de esta manera tiene la ventaja de que no necesita pasar la instancia que desea modificar como argumento para un método. Para clases como String o Integer, preferiría usar una clase wrapper. Luego puede controlar cuándo se crea dicho objeto (por el constructor de la clase wrapper o uno de los métodos de la clase). Si usa la clase Integer, es mucho más complicado controlar esto, ya que todos pueden crear una instancia de ella.

Por otro lado, su ejemplo se considera para algunas clases de utilidad como StringUtils del paquete apache commons-lang. Solo eche un vistazo a esto, ya que creo que quería crear algo como esto. (No reinventes la rueda)

new String () es un olor a código: es casi siempre innecesario debido a la inmutabilidad de String . Una vez que una instancia de String tiene un valor, esa instancia nunca tendrá un valor diferente.

En el siguiente método, new String () es redundante:

public static String string( final String string ) {
    return new String( string.toLowerCase() );
}

toLowerCase () devuelve una nueva (diferente) instancia de String - new String () no hace nada beneficioso aquí aparte de la causa otra creación de objeto que tiene el valor exacto de la instancia String devuelta por toLowerCase()

Aquí hay un pequeño script Groovy que muestra el concepto (espero, tenga en cuenta que esto es Java bajo el lenguaje de script):

String a = 'CamelCase'
String b = a.toLowerCase()

println "a='${a}'"
println "b='${b}'"

produciendo

a='CamelCase'
b='camelcase'

Tenga en cuenta que a no cambió : es inmutable; b es un nuevo valor de String .

Lo mismo es cierto para BigDecimal.movePointLeft () o cualquier otro método en BigDecimal que devuelve un BigDecimal : son instancias nuevas , dejando la instancia original sin cambios.

OK, ahora para responder tu pregunta:

Tener una serie de operaciones para Strings que realicen un propósito útil en su aplicación es una buena idea. El uso de una fábrica probablemente no sea necesario para algo como String , pero podría ser para una clase inmutable diferente que requiere cierto esfuerzo para construir.

En el caso de que no sea posible extender la clase base, como String , una clase de método estático como @kgiannakakis describe está bien.

De lo contrario, si el " inmutable " class es parte de la aplicación, donde tiene acceso a la declaración / definición de clase, los métodos que devuelven una nueva instancia, en el modelo de BigDecimal , String , etc., serían preferible. Esto es en esencia lo que @Erik Hesselink, @ Bruno Conde y @reallyinsane han dicho.

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