Plateforme Level Extreme
Abonnement
Profil corporatif
Produits & Services
Support
Légal
English
Orientación a Objetos eXtrema en Visual FoxPro
Message
 
 
À
28/02/2005 12:26:30
Information générale
Forum:
Visual FoxPro
Catégorie:
Programmation Orientée Object
Divers
Thread ID:
00991259
Message ID:
00992068
Vues:
23
Hola, Ricardo.

ATENCION: Respuesta bastante larga.

>Acabo de leer el artículo de Martín Salías al que hago referencia en el título, correspondiente a la edición de UTMag de febrero 2005.
>Pese a que el título del artículo aclara que se trata de Visual FoxPro, me hubiera gustado que apareciera algo de código acompañando los conceptos y los diagramas UML, porque habemos todavía desarrolladores que, aunque aparentemente creemos haber entendido perfectamente los conceptos y haber interpretado correctamente los diagramas, a la hora de la verdad no sabemos como volcarlo al código.
>Suponiendo que tal vez Martín tuvo la idea de dejarnos una tarea para el hogar, cuya resolución aparecerá en el próximo número [;-D], pongo a consideración de ustedes lo que creo es el código que correspondería a los diagramas, esperando, de ser posible, alguna devolución de parte de quienes tienen más experiencia en el tema.
>Creo que el concepto de "herencia", a esta altura, lo tengo bastante claro. Tengo mis dudas con respecto a la "composición", no tanto en cuanto al concepto en sí, sino más bien en el código que la representa.

Bueno, en líneas generales está bien, pero -y acá es donde escribir el código refina el diseño- aparecen algunos problemas durante la resolución.

Primero va mi versión refactorizada (y con una pequeñísima prueba al comienzo) de tu definición de clases (ver mensaje de Ricardo para leer el código original):
* Version 1 - Duplication smell

oDoc = CreateObject( "Doctor" )
oDoc.cName = "Cureta"

oPatient = CreateObject("LawStudent" )
oPatient.cName = "Ricardo"

oDoc.oVocation.Operate( oPatient )


*=====================================
Define Class Person As Session
	cName  = ""
	cSex   = "F"
	dBirth = Date()
	
	*--------------------------
	Function Age( tdDate As Date ) As Number

		Return Floor( ( tdDate - this.dBirth ) / 365.25 )
	Endfunc
Enddefine

*=====================================
Define Class Professional As Person
	cTitle           = ""
	lAvailable       = .T.
	oVocation       = Null
	
	*--------------------------
	Function Hire() As Boolean
		Local llReturn as Boolean
		 
		llReturn = this.lAvailable
		
		If llReturn
			this.lAvailable = .f.
		EndIf 
		
		Return llReturn
	Endfunc
Enddefine

*=====================================
Define Class Student As Person
	cUniversity      = ""
	cCareer          = ""
	oVocation       = Null

Enddefine

*=====================================
Define Class Vocation As Session
	cName = ""
Enddefine

*=====================================
Define Class Medicine As Vocation
	cName = "Medicine"
	
	*--------------------------
	Function Operate( oPerson As Person ) As Void
	EndFunc
Enddefine

*=====================================
Define Class Law As Vocation
	cName = "Law"
Enddefine

*=====================================
Define Class LawStudent As Student
	
	*-------------------------
	Function Init() as Boolean
		If DoDefault()
			this.oVocation = CreateObject( "Law" )
		EndIf 
	EndFunc  
Enddefine

*=====================================
Define Class Lawyer As Professional

	*-------------------------
	Function Init() as Boolean
		If DoDefault()
			this.oVocation = CreateObject( "Law" )
		EndIf 
	EndFunc  
Enddefine

*=====================================
Define Class MedicineStudent As Student

	*-------------------------
	Function Init() as Boolean
		If DoDefault()
			this.oVocation = CreateObject( "Medicine" )
		EndIf 
	EndFunc  
Enddefine

*=====================================
Define Class Doctor As Professional

	*-------------------------
	Function Init() as Boolean
		If DoDefault()
			this.oVocation = CreateObject( "Medicine" )
		EndIf 
	EndFunc  
Enddefine
Fuera de eliminar comentarios (no puedo evitarlo), implementé el método Age para que devuelva la edad y Hire para que contrate a un profesional disponible (al contratarlo deja de estar disponible). Es poco, pero así tenés algo para jugar.

Otro cambio menor: agregué en la implementación un prefijo de tipo a las propiedades para no "chocar" con la propiedad Name, por ejemplo (cosas del Fox).

El cambio más importante, finalmente, es que eliminé tus propiedades Class/ClassLibrary (que huelen a TierAdapter). ¿Por qué? Por un lado para mostrar otra alternativa, y por otro para hacer que las instanciaciones de objetos sean explícitas. El motivo fundamental es tratar de eliminar una dependencia muy foxera en el modelo, que es la posibilidad de resolver la clase a crear en tiempo de ejecución, ya que esto no se puede hacer (en forma sencilla y directa) en otros entornos (Java/.NET) y por lo tanto, prefiero evitarlo y que mi código sea lo más "portable" posible.

Ahora volvamos al problema de esta implementación, que es el mismo aroma sospechoso que tenía la tuya (y que está sugerido en mi diagrama http://www.salias.com.ar/images/xoop3.gif) es que hay cierta duplicación en el sentido en que tanto Professional como Student necesitan una manera de obtener su Vocation (en el ejemplo, Law ó Medicine).

En tu versión, ambas clases (por separado) definían las propiedades Class/Library, e instanciaban la vocación en el Init (mismo código en ambos casos). En mi versión, tengo que extender cada Init en las hojas para que instancie la vocación correspondiente.

Así que nos ponemos el sombrero de Refactoring y empezamos a analizar posibilidades. Básicamente se me ocurren enseguida dos:

La primera sería utilizar abstracción: agrego una subclase abstracta de Person, que podríamos llamar EducatedPerson, de la que voy a hacer derivar Professional y Medicine, alargando la jerarquía. El argumento para esto es que poner el método que cargue la vocación directamente en Person pareciera ser darle a esta clase demasiada responsabilidad.

La segunda es un contra-argumento de la primera. ¿Cuántas subclases tenemos de Person? Por ahora sólo estas dos, y ambas necesitan el método para crear la vocación, así que me inclino por esta, dejando para más adelante (si realmente hiciese falta) la posibilidad de estirar la jerarquía.

Código refactorizado:
* Version 2 - Duplication eliminated

oDoc = CreateObject( "Doctor" )
oDoc.cName = "Cureta"

oPatient = CreateObject("LawStudent" )
oPatient.cName = "Ricardo"

oDoc.oVocation.Operate( oPatient )


*=====================================
Define Class Person As Session
	cName  = ""
	cSex   = "F"
	dBirth = Date()
	oVocation = Null
	
	*--------------------------
	Function Init() as Boolean
		this.oVocation = this.SetVocation()
	EndFunc 
	
	*--------------------------
	Function Age( tdDate As Date ) As Number

		Return Floor( ( tdDate - this.dBirth ) / 365.25 )
	EndFunc
	
	*--------------------------
	Function SetVocation() as Object 
		Return null
	EndFunc 
Enddefine

*=====================================
Define Class Professional As Person
	cTitle           = ""
	lAvailable       = .T.
	
	*--------------------------
	Function Hire() As Boolean
		Local llReturn as Boolean
		 
		llReturn = this.lAvailable
		
		If llReturn
			this.lAvailable = .f.
		EndIf 
		
		Return llReturn
	Endfunc
Enddefine

*=====================================
Define Class Student As Person
	cUniversity      = ""
	cCareer          = ""
Enddefine

*=====================================
Define Class Vocation As Session
	cName = ""
Enddefine

*=====================================
Define Class Medicine As Vocation
	cName = "Medicine"
	
	*--------------------------
	Function Operate( oPerson As Person ) As Void
	EndFunc
Enddefine

*=====================================
Define Class Law As Vocation
	cName = "Law"
Enddefine

*=====================================
Define Class LawStudent As Student
	
	*--------------------------
	Function SetVocation() as Object 
		Return CreateObject( "Law" )
	EndFunc 
Enddefine

*=====================================
Define Class Lawyer As Professional

	*--------------------------
	Function SetVocation() as Object 
		Return CreateObject( "Law" )
	EndFunc 
Enddefine

*=====================================
Define Class MedicineStudent As Student

	*--------------------------
	Function SetVocation() as Object 
		Return CreateObject( "Medicine" )
	EndFunc  
Enddefine

*=====================================
Define Class Doctor As Professional

	*--------------------------
	Function SetVocation() as Object 
		Return CreateObject( "Medicine" )
	EndFunc  
Enddefine
El refactoring utilizado es "Pull Up Method" (subir método; http://www.refactoring.com/catalog/pullUpMethod.html). Notese un detalle: aunque pareciera que en las clases hoja (estudiantes de leyes y medicina, doctor y abogado) estoy reescribiendo el método SetVocation, aquí no hay duplicación, ya que esto es totalmente declarativo. No lo hago con una propiedad para evitar una construcción que es muy Fox-dependiente, pero el criterio sigue siendo el mismo. Tengo un método que debe devolverme la instancia de la vocación que necesito.

Nota: todo el código y nombres están en inglés, porque uso mis ejemplos en distintos contextos y no puedo hacer versiones diferentes, sobre todo porque el código va mutando y mantenerlo sincronizado es casi imposible. Trato de usar siempre nombres y expresiones lo más fáciles de entender. Si alguien tiene dudas, puede preguntar y lo aclaro con gusto.

Espero que se entienda la idea. Escucho sugerencias y/o comentarios.

Saludos,
Précédent
Suivant
Répondre
Fil
Voir

Click here to load this message in the networking platform