Implementación Factory Method - lenoryv/Design-Patterns GitHub Wiki

Supongamos que desea crear aplicaciones que presenten varios documentos al usuario que se puedan utilizar en una interfaz gráfica de usuario. Empiece por crear una aplicación que se ocupe de dibujar documentos. Luego, desea crear un documento de Word, pero todo su código base está acoplado con el documento de dibujo, por lo que debe agregarle cambios. Luego, desea crear otro tipo de documento y necesitará más cambios y pronto las cosas se complicarán.

Así que vamos a abstraer las clases Aplication y Document. La clase Aplicación será responsable de administrar los documentos y crearlos según sea necesario, por ejemplo, cuando el usuario selecciona de un menú para abrir un documento o crear uno nuevo.

Y cada documento que se creará será una subclase de la clase abstracta Document. La instanciación de cada subclase de documento es específica de la aplicación. Esto significa que los documentos de dibujo se tratarán con la aplicación de dibujo y los documentos de Word se tratarán con la aplicación de Word, etc.

Cuando el cliente, que es la GUI, le pide a la clase de aplication que cree un nuevo documento, la clase de aplicación no tiene idea de qué tipo de documento crear. Pero el cliente debe crear instancias de clases, pero solo conoce las clases abstractas, que no se pueden instanciar.

Pero aquí viene el patrón del Factory Method para el rescate. Encapsula el conocimiento para crear la subclase de documento adecuada para cada aplicación específica y saca este conocimiento de la GUI.

Implementación

Ahora intentemos implementar el ejemplo de Aplicación y Documento en código usando el lenguaje Kotlin.

El objetivo de este sencillo programa es imprimir la información del documento específico. Tenemos la interfaz Document y sus subclases concretas DrawingDocument y WordDocument.

interfaz Documento {
     fun  showDocumentInfo () 
} clase DrawingDocument : Documento {
     anular fun showDocumentInfo () { 
        println ( "Este es un documento de dibujo" ) 
    } 
} clase WordDocument : Documento {
     anular fun showDocumentInfo () { 
        println ( "Este es un documento de Word " ) 
    } 
}

DrawingApplication y WordApplication implementan el método de fábrica createDocument() en la clase abstracta Application. La clase Application también tiene una función getDocument() que será utilizada por el cliente para solicitar el documento específico.

 clase  abstracta Aplicación {
     abstract  fun  createDocument (): 

    Objeto complementario del documento { diversión getApplicationDocument (documentType: DocumentType): Aplicación {
             return when (documentType) { 
                DocumentType.Drawing -> DrawingApplication () 
                DocumentType.Word -> WordApplication () else -> throw Excepción ( "tipo de documento no válido" ) 
            } 
        } 
    } 
} clase DrawingApplication : Application () {
     anular
          
                

  fun  createDocument () = DrawingDocument () 
} clase WordApplication : Application () {
     anular fun createDocument () = WordDocument () 
}

Para el parámetro documentType de la función getApplicationDocument() es de tipo DocumentType, que es una clase de enumeración que se utilizará para determinar qué documento de aplicación obtener.

enum  class  DocumentType {Drawing, Word}

Codigo completo:

interfaz Documento {
     fun  showDocumentInfo () 
} clase DrawingDocument : Documento {
     anular fun showDocumentInfo () { 
        println ( "Este es un documento de dibujo" ) 
    } 
} clase WordDocument : Documento {
     anular fun showDocumentInfo () { 
        println ( "Este es un documento de Word " ) 
    } 
} Aplicación de clase abstracta {
     abstract fun createDocument (): Documento

    objeto complementario { fun getApplicationDocument (documentType: DocumentType): Application {
             return when (documentType) { 
                DocumentType.Drawing -> DrawingApplication () 
                DocumentType.Word -> WordApplication () else -> throw Exception ( "Tipo de documento no válido" ) 
            } 
        } 
    } 
} clase DrawingApplication : Application () {
     anular diversión createDocument () = DrawingDocument () 
} clase WordApplication : Application () {
    

 override  fun  createDocument () = WordDocument () 
} enumeración class DocumentType {Drawing, Word} fun main () {
     val drawingApplication = Application.getApplicationDocument (DocumentType.Drawing)
     val drawingDocument = drawingApplication.createDocument () 
    drawingDocument.showDocumentInfo () val wordApplication = Application.getApplicationDocument (DocumentType.Word)
     val wordDocument = wordApplication.createDocument () 
    wordDocument.showDocumentInfo () 
}

La salida será la siguiente:

Este es un documento de Drawing

Este es un documento de Word

Example

sealed class Country {
    object USA : Country() //Kotlin 1.0 could only be an inner class or object
}

object Spain : Country() //Kotlin 1.1 declared as top level class/object in the same file
class Greece(val someProperty: String) : Country()
data class Canada(val someProperty: String) : Country() //Kotlin 1.1 data class extends other class
//object Poland : Country()

class Currency(
    val code: String
)

object CurrencyFactory {

    fun currencyForCountry(country: Country): Currency =
        when (country) {
            is Greece -> Currency("EUR")
            is Spain -> Currency("EUR")
            is Country.USA -> Currency("USD")
            is Canada -> Currency("CAD")
        }  //try to add a new country Poland, it won't even compile without adding new branch to 'when'
}

Usage

val greeceCurrency = CurrencyFactory.currencyForCountry(Greece("")).code
println("Greece currency: $greeceCurrency")

val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA).code
println("USA currency: $usaCurrency")

assertThat(greeceCurrency).isEqualTo("EUR")
assertThat(usaCurrency).isEqualTo("USD")

Output

Greece currency: EUR

US currency: USD

UK currency: No Currency Code Available