2010-09-19 11 views
11

Estoy intentando escribir un programa en estilo orientado a objetos. Tengo algunas confusiones al codificar la interacción entre dos objetos.Programación de estilo orientado a objetos para la interacción entre objetos

Escenario: Persona (John) da Persona (Betty) $ 5.

soluciones posibles (pseudo código):

A) John.pays (Betty, 5);
B) Betty.receives (John, 5);
C) Bank.transfer (John, Betty, 5);
D)
begin transaction:
John.decrease (5);
Betty.increase (5);
transacción final:
E) Service.transferMoney (John, Betty, 5); // El servicio es un objeto de servicio genérico

Por favor dígame cuál es una forma más apropiada de codificación en modo OOP, y la razón detrás de ello. Estoy buscando algunas pautas como la regla "Tell, Do not Ask".

Gracias.

+0

Esta pregunta parece estar fuera del tema porque se trata de la revisión del código y cabría en el código review.stackexchange.com –

Respuesta

0

Mi voto: C. Donde C hace lo que hace D (por ejemplo, no pierde dinero, etc.).

En este pequeño ejemplo, "el banco" es una entidad perfectamente válida que sabe cuánto dinero tienen John y Betty. Ni John ni Betty deberían poder mentirle al banco.

No tenga miedo de invertir (o no) la lógica en un programa "OO" según sea necesario para la situación.

0

Debe modelar de acuerdo a su dominio. La opción C parece la mejor opción, ya que separará la lógica de transacción en la clase Bank \ Service.

0

Esta es una pregunta que a menudo me cuesta trabajo como programador novato. Estoy de acuerdo con que "C" parece ser la mejor opción. En algo como esto, creo que es mejor usar una entidad "neutral" como el "banco". Esto en realidad modela la mayoría de las transacciones de importancia reales ya que la mayoría de las transacciones de importación utilizan cheques y/o crédito (un tercero neutral).

2

votaría por ninguno de los anteriores :)

¿Por qué John pagando Betty? Esa es una pregunta importante, ya que explica dónde está el punto de entrada. Digamos que John le debe dinero a Betty, y es día de pago.

public class John 
{ 
    public void onPayday() 
    { 
     Betty.Receive(5.0f); 
    } 
} 

Esto es si quieres ir con un enfoque de estilo puro de interacción de objetos, por supuesto.

La diferencia aquí es que no tenemos una rutina externa que coordine las interacciones entre John y Betty. En cambio, tenemos a John respondiendo a eventos externos y eligiendo cuándo interactuar con Betty. Este estilo también conduce a descripciones muy sencillas de la funcionalidad deseada, por ejemplo, "en el día de pago, John debe pagarle a Betty".

Este es un buen ejemplo de lo que significa Inversión de control: los objetos interactúan entre sí, en lugar de ser manipulados por alguna rutina externa. También es un ejemplo de Tell, Do not Ask, ya que los objetos son diciendo entre sí cosas (a John le dijeron que es día de pago, John le dice a Betty que acepte 5 dólares).

+0

_ @ kyoryu_: Esto suena muy bien, pero ¿dónde sucede * la transferencia de dinero real *, y ¿Cómo se vería ese código? Según entiendo tu respuesta, 'Betty.Receive (5.0f)' en realidad no transfiere el dinero, sino que solo desencadena un evento ("alguien pregunta betty para recibir 5 $"). De lo contrario (si esa llamada a un método hace la transferencia de dinero), entonces seguramente está votando por la opción B de OP? – stakx

+0

@stakx: es un ejemplo simplificado para mostrar la interacción de objetos. La principal diferencia entre esta opción y la Opción B es que en la Opción B, todavía hay un procedimiento de nivel superior que manipula a John y Betty, y que actúa como intermediario. En esta opción, John y Betty están * directamente * interactuando, sin necesidad de un intermediario en absoluto. El código verdadero para esto también incluiría a John deduciendo sus $ 5, y comprometiéndose o retrocediendo cuando Betty lo recibió con éxito, algo como el caso descrito aquí: http://www.eaipatterns.com/ramblings/18_starbucks.html – kyoryu

2

Hay una serie de soluciones alternativas aquí. Por ejemplo,

Betty.Receieves(John.Gives(5))

Esto supone que el Da función devuelve la cantidad dada.

tx = CashTransaction(John, Betty); 
tx.Transfer(5); 

Esto supone la primera prameter es el responsable del pago, y el segundo es el beneficiario, a continuación, puede realizar múltiples transacciones sin crear nuevos objetos.

Las cosas se pueden modelar de varias maneras. Debe elegir el que más se parezca a lo que está tratando de modelar.

+0

Esto haría El método Gives() está altamente acoplado en el método Receives()(). – janetsmith

+0

No, no es así. Da solo un valor, a John no le importa a quién se lo está entregando. Betty, del mismo modo, solo recibe un número ... y no le importa quién es el que se lo da. –

+0

En realidad, el método Gives/Receives está tan desacoplado, que tiene un poco de fugas. Puede pasarle cualquier número, y los números pueden perderse si no son "recibidos". En la vida real, corregirías esto a través de clases abstractas, polimorfismo o interfaces. Sin embargo, en tales construcciones simples uno tiene que suponer que la seguridad y la robustez no son problemas. –

1

Si usted realmente desea conseguir OOPy, intente lo siguiente

Person  Betty,John; 
CashTransfer PocketMoney; 
PocketMoney.from = John; 
PocketMoney.to  = Betty; 
PocketMoney.amount = 20.00; 
PocketMoney.transfer(); 

El punto de programación orientada a objetos no es para hacer el código más como el lenguaje escrito, pero para tener objetos con diferentes métodos y parámetros para aprovechar el código sea más legible.

Por lo tanto, a partir del código anterior, puede ver que John le está dando a Betty $ 20 en dinero de bolsillo. El código es significativo, lo que permite una lectura más fácil del código, así como la comprensión.

+1

Esto no es OOPy. Esto es ofuscante. En particular tener un objeto en un estado no muerto inutilizable después de la construcción es, francamente, malvado. Los objetos deberían nacer en condiciones utilizables, no en estado de no muerte. –

+1

Pero las personas podrían tener métodos como Person :: AddFunds(), etc. el ejemplo es extremadamente primitivo. Al tener un objeto separado para la transferencia de efectivo, se le da el significado de transferencia, así como también se le da al transferidor la posibilidad de utilizarlo. innumerables veces, PocketMoney.transfer() hará que John dé a Betty $ 20. –

+0

Te estás perdiendo mi punto. Está creando instancias de CashTransfer (PocketMoney). Esa clase instanciada es undead. Tienes que llenarlo con los cerebros de John y Betty y algo de dinero antes de que sea una clase completamente viva en la que puedas invocar el método 'transfer()'. John y Betty deberían estar en el constructor, IMO, y la cantidad debe estar en la llamada 'transfer'. –

10

Una cosa que he notado es que las personas que son nuevas en OOP quedan atrapadas tratando de mapear el mundo físico en el código que están escribiendo. ¿De verdad te importa que John y Betty sean personas o realmente quieres representar una cuenta bancaria? Creo que la elección de objetos en el ejemplo hace que sea más difícil encontrar la solución al problema.

Las partes importantes de esto son 1) Dónde ubicar la lógica de cómo mover el dinero. 2) Dónde almacenar los datos de cuánto dinero tiene cada persona.

Debe decidir si desea hablar sobre el problema en el contexto de una persona o un cliente de un banco (puede ser una persona, empresa u otra cosa). Supongo que estás hablando de un cliente porque asumir que es una persona sería limitante y engañoso. Además, un banco es un término bastante genérico, es el gran edificio de ladrillo con personas dentro de él o es el sitio web en línea con varias páginas diferentes que hacen cosas diferentes. Un objeto de cuenta bancaria puede tener un método (posiblemente estático según cómo decida almacenar sus datos y para qué va a usar su objeto) que sepa cómo transferir de una cuenta a otra. La lógica de cómo transferir no pertenece a Betty o John o a un banco, pertenece a una cuenta bancaria que puede tener una lógica especial basada en el tipo de cuenta si hay cargos involucrados o similar. Si diera esa lógica al banco, terminaría con una clase bancaria gigante con métodos para todo, desde invitar a un cliente hasta tratar con dinero en tipos de cuenta muy específicos. Cada tipo de cuenta tiene diferentes reglas sobre cómo maneja las transferencias. Piense en los momentos en los que es posible que desee mostrar una transferencia o depósito pendiente.

Si solo está resolviendo el problema de la transferencia de dinero, no es necesario crear muchos objetos. Con base en los requisitos conocidos y los supuestos requisitos futuros, la siguiente sería una buena opción. Cuenta de cheques.Transfer (johnsAccountNo, bettysAccountNo, cantidad)

+0

+1, ya que puede haber una compañía detrás de una cuenta bancaria en lugar de una persona. – chelmertz

+2

Bueno, ya que la idea fundadora de OO fue la capacidad de modelar el mundo real para que la gente que intentaba modelar el mundo real lo haya hecho bien. Dejar que el diseño comience en el modelo mental de los usuarios también es una idea comúnmente enseñada (y es parte de la base de MVC) –

+1

@runefs sí, estoy de acuerdo. Solo quiero decir que puedes ponerte en una situación en la que lo hayas llevado muy lejos. –

3

¿Puedo hacer una pregunta ahora? ¿Quién controla el dinero? ¿Decide John el monto de la transacción, Betty o alguna tercera parte no especificada?

La razón por la que estoy preguntando es porque aquí no hay una respuesta correcta o incorrecta, solo una que podría ser más flexible o más sólida que las demás. Si esta es una situación de la vida real, modelaría la transacción como algo en lo que ambas partes deben ponerse de acuerdo antes de proceder, y la persona que gasta el dinero (John) en iniciarla. Algo como respuesta C y @Mysterie hombre

tx transaction_request = John.WantsToBuyFor(5); //check if John can 
if(Betty.AgreesWith(transaction_request))  //check if Betty wants 
{ 
    transaction_request.FinalizeWith(Betty);  //Do it with Betty 
} 

y la función FinalizeWith hace los cálculos

void FinalizeWith(Person party) 
{ 
    requestor.cash -= amount; 
    party.cash += amount; 
{ 

Por supuesto, es posible que desee añadir un poco de descripción de lo que el tema es John compra.

+2

+1, no para el ejemplo de código concreto, sino por la línea de pensamiento (un controlador y dos partes que ambos deben aceptar antes de que el controlador haga su trabajo). Y para la transacción que "lo hacen con Betty" :) – stakx

3

La respuesta a esta pregunta es larga y complicada que obtendrá en partes de un gran número de personas. ¿Por qué solo en pedazos? Porque la respuesta correcta depende casi por completo de cuáles son los requisitos de su sistema.

Una trampa en la que deberá asegurarse de no caer, sin embargo, es this one. Lee las respuestas que obtienes aquí. Obtendrás muchos buenos consejos. (Preste la mayor atención a los consejos que se han votado mucho.) Una vez que haya leído y entendido ésos, read Steve Yegge's rant (¡y entiéndelo!) También. Te salvará la cordura en el largo plazo.

0

ser nuevo en programación orientada a objetos y, finalmente, el uso de algunos programación orientada a objetos, diría que debería ser A y B.

Nos estamos centrando en las personas y que depende de cada persona para manejar su dinero. No sabemos si va a usar el banco o si solo recibe efectivo directamente de Betty.

Creó una clase Persona y agrega métodos a la clase con dos métodos: enviar y recibir. También debe tener un saldo público var nombrado para realizar un seguimiento de sus saldos.

Crea dos objetos Person: Betty y John. Use los métodos en consecuencia. Como John.sends (Betty, 5). Eso debería crear Betty y actualizar el equilibrio de Betty también.

¿Qué sucede si quieren usar el banco? Agregue otro método, por ejemplo ... Transferir (acct) sea lo que sea.

Eso es lo que yo pensaría.

2

Hay una propiedad de OOP puro que puede ayudar con el ejemplo que pasa fácilmente por debajo del radar, pero el modelo object-capability lo hace explícito y se centra en. El documento vinculado ("De objetos a capacidades" (FOtC)) entra en el tema en detalle, pero (en resumen) el punto de las capacidades es que la capacidad de un objeto para afectar su mundo se limita a los objetos a los que tiene referencia. Eso puede no parecer significativo al principio, pero es muy importante cuando se trata de proteger el acceso y afecta qué métodos de una clase están disponibles en los métodos de otras clases.

La opción A) da acceso a la cuenta John a la cuenta Betty, mientras que la opción B) le da a Betty acceso a la cuenta John; ninguno es deseable. Con la opción C), el acceso a la cuenta está mediado por un banco, por lo que solo los bancos pueden robar o falsificar dinero. La opción D) es diferente de las otras tres: las otras muestran un mensaje que se envía pero no la implementación, mientras que D) es una implementación de método que no muestra qué mensaje maneja ni a qué clase lo maneja. D) podría ser fácilmente la implementación de cualquiera de las tres primeras opciones.

FOTC tiene el principio de una solución que incluye algunas otras clases:

  • selladores & unsealers,
  • bolsos, que son un poco como las cuentas en que contienen dinero, pero no necesariamente tienen un dueño.
  • pastillas de menta, que son las únicas cosas que pueden crear carteras con saldos positivos

una pastilla de menta tiene un/par unsealer sellador, lo que dota a un bolso cada vez que la menta crea uno. Los monederos supervisan los cambios de saldo; usan el sellador cuando disminuyen el equilibrio, y el que se destrama para transferir de un bolso a otro. Los monederos pueden generar carteras vacías. Debido al uso de selladores & unsealers, un bolso solo funciona con otros bolsos creados por la misma menta. Alguien no puede escribir su propio bolso para falsificar dinero; solo un objeto con acceso a una casa de moneda puede crear dinero. La falsificación se previene limitando el acceso a las mentas.

Cualquier persona con acceso a un bolso puede iniciar una transacción al generar un bolso vacío y transferir dinero desde el primer bolso en el mismo. El bolso temporal se puede enviar a un destinatario, que puede transferir dinero del bolso temporal a algún otro bolso que posea. El robo se previene limitando el acceso a carteras. Por ejemplo, un banco tiene carteras en nombre de clientes en cuentas. Como un banco solo tiene acceso a los monederos de las cuentas de sus clientes y carteras temporales, solo el banco de un cliente puede robarle al cliente (aunque tenga en cuenta que en una transferencia entre cuentas bancarias, hay dos clientes que pueden ser víctimas, de ahí dos posibles ladrones).

A este sistema le faltan algunos detalles importantes, como las autoridades monetarias (que tienen referencias a una o más casas de moneda) para crear dinero.

En general, las transacciones monetarias son difíciles de implementar de forma segura, y por lo tanto, pueden no ser los mejores ejemplos para aprender los conceptos básicos de OOP.

+1

+1 gran explicación de las cuatro opciones en el segundo párrafo. El resto de su respuesta tal vez se centre en la seguridad más de lo necesario, pero esta es una lectura muy interesante, sin embargo. – stakx

Cuestiones relacionadas