OO vs. Layered; equilibrio & # 8220; pureza OO & # 8221; con hacer las cosas

StackOverflow https://stackoverflow.com/questions/153394

  •  03-07-2019
  •  | 
  •  

Pregunta

Creo en OO, pero no hasta el punto en el que los diseños / implementaciones inapropiados se deban usar solo para ser "compatible con OO".

Entonces, cómo tratar con la arquitectura en capas Serlvet / EJB / DataContainer:

  • Los servlets reciben solicitudes y llaman a una " capa de negocios " (por ejemplo, EJB de sesión)
  • La capa empresarial ubica DataContainers desde la base de datos y los manipula para implementar la lógica de negocios
  • Los DataContainers no contienen código real, solo obtenga / establezca los correspondientes a la base de datos.

Este enfoque tiene atractivo; Los DataContainers tienen claro lo que hacen y es muy fácil saber de dónde provienen los datos.

Además de no ser OO, esto lleva a clases poco claras de Capa de Negocios que pueden ser difíciles de nombrar y de organizar.

Incluso si estuviéramos tratando de ser más " OO " (por ejemplo, al poner algunos de estos métodos en los DataConatiners), algunas de estas operaciones operan en más de un conjunto de datos.

¿Cómo evita que su capa de negocios se vuelva confusamente procedimental, pero sin contaminar sus DataContainers con lógica de negocios?

Ejemplo

class UserServlet {
  handleRequest() {
    String id = request.get("id");
    String name = request.get("name");
    if (UserBizLayer.updateUserName(id,name))
      response.setStatus(OK);
    else
      response.setStatus(BAD_REQUEST);
  }
}

class UseBizLayer {
    updateUserName(String id, String name) {
        long key = toLong(id);
        user = userDAO.find(key);
        if user == null
            return false;
        if (!validateUserName(name))
            return false;
        user.setName(name);
        userDAO.update(user);
        return true;
    }

    validateUserName(String name) {
        // do some validations and return
    }
}

class User {
    long key;
    String name;
    String email;

    // imagine getters/setters here
}
  • No queremos que validateUserName en el usuario, ya que solo opera con un nombre; Supongo que podría ir a otra clase, pero luego tenemos otro de procedimiento " uti " tipo de clase
  • No queremos métodos de persistencia en el Usuario, ya que hay valor en el desacoplamiento de las estructuras de datos de su estrategia de persistencia
  • No queremos lógica de negocios en nuestro Servlet, ya que es posible que necesitemos reutilizar esa lógica en otro lugar
  • No queremos nuestra lógica de negocios en nuestro Usuario, ya que esto se basa demasiado en la clase de Usuario, lo que dificulta la reutilización de la lógica de negocios y une al usuario con su estrategia de persistencia

Me doy cuenta de que este ejemplo no es tan malo, pero imagina 10 DataContainers y 20 objetos BizLayer con varios métodos cada uno. Imagina que algunas de esas operaciones no están "centradas" en un contenedor de datos en particular.

¿Cómo podemos evitar que esto sea un problema de procedimiento?

¿Fue útil?

Solución

Así que abordaré mis pensamientos sobre esto en unos pocos puntos:

  1. Parece que en un sistema Java EE, en algún momento usted tiene que lidiar con la plomería de Java EE, la plomería no siempre se beneficia de los conceptos OO, pero sí puede hacerlo con un poco de creatividad y trabajo. Por ejemplo, podría aprovecharse de cosas como AbstractFactory, etc. para ayudar a generalizar la mayor cantidad posible de esta infraestructura.
  2. Gran parte de lo que estás investigando se analiza en el excelente libro de Eric Evans llamado Domain Driven Design. Le recomiendo que lo mire, ya que aborda el problema de expresar el conocimiento del dominio y tratar con la infraestructura técnica que lo respalda.
  3. Después de leer y hacer GROKD algo de DDD, encapsularía mi infraestructura técnica en repositorios. Todos los repositorios se escribirían para utilizar una estrategia de persistencia basada en los EJB de sesión. Escribiría una implementación predeterminada para que sepa cómo comunicarse con su sesión EJBS. Para hacer que esto funcione, debe agregar un poco de convención y especificar esa convención / contrato en sus interfaces. Los repositorios hacen todo el CRUD y solo deberían hacer más que eso si es absolutamente necesario. Si dijera "Mis DAOS son mis repositorios", entonces estaría de acuerdo.
  4. Así que para continuar con esto. Necesita algo para encapsular la unidad de trabajo que se expresa en UseBizLayer. En este nivel, creo que la naturaleza de esto es que estás atascado escribiendo un código que será un script de transacción. Estás creando una separación de responsabilidad y estado. Esto es típicamente como lo he visto hecho dentro de los sistemas Java EE como un tipo predeterminado de arquitectura. Pero no está orientado a objetos. Intentaría explorar el modelo y ver si al menos podría intentar poner en común algunos de los comportamientos que están escritos en las Clases de Negocios.
  5. Otro enfoque que he usado antes es deshacerme de las clases de BizLayer y luego enviar las llamadas del Dominio a los Repositorios reales / DAO que realizan la operación. Sin embargo, esto podría requerir alguna inversión en la construcción de infraestructura. Pero podría hacer mucho con un marco como Spring y usar algunos conceptos de AOP para que esto funcione bien y reducir la cantidad de infraestructura personalizada que se necesita.

Otros consejos

ya que estás implementando clases y objetos, tu solución será OO, independientemente de cómo coloques las cosas. ¡Es posible que no esté muy bien estructurada dependiendo de tu situación / necesidades! ;-)

en cuanto a su pregunta específica, en algunos casos tendría sentido que validateUserName pertenezca a la clase de usuario, ya que cada usuario desea tener un nombre válido. O puede tener una clase de utilidad de validación suponiendo que otras cosas tienen nombres que usan las mismas reglas de validación. Lo mismo ocurre con el correo electrónico. Podría dividirlos en las clases NameValidator y EmailValidator, lo que sería una buena solución si se usaran mucho. También puede proporcionar una función validateUserName en el objeto Usuario que acaba de llamar el método de clase de utilidad. Todas estas son soluciones válidas.

Una de las grandes alegrías de OOD / OOP es que cuando el diseño es correcto, sabes que es correcto, porque muchas cosas simplemente se salen del modelo y puedes hacer eso. no podías hacerlo antes.

En este caso, crearía las clases NameValidator y EmailValidator, porque parece probable que otras entidades tengan nombres y direcciones de correo electrónico en el futuro, pero proporcionaría las funciones validateName y validateEmailAddress en la clase User porque eso hace que sea una interfaz más conveniente para los objetos biz a utilizar.

el resto de las viñetas "no queremos" son correctas; no solo son necesarios para las capas adecuadas, sino que también son necesarios para un diseño OO limpio.

las capas y OO van de la mano en el guante en función de una separación de preocupaciones entre las capas. Creo que tienes la idea correcta, pero necesitarás algunas clases de utilidad para las validaciones comunes tal como se presentan

Piense en cómo se realizarían estas tareas si no hubiera una computadora y modele su sistema de esa manera.

Ejemplo simple ... El cliente completa un formulario para solicitar un widget, se lo entrega a un empleado, el empleado verifica la identidad del cliente, procesa el formulario, obtiene un widget, entrega el widget y un registro de la transacción a la cliente y mantiene un registro de la transacción en algún lugar de la empresa.

¿El cliente almacena sus datos? No, el empleado lo hace. ¿Qué papel está tomando el empleado cuando está almacenando los datos del cliente? Administrador de registros del cliente.

¿El formulario verifica que se completó correctamente? No, el empleado lo hace. ¿Qué papel está tomando el empleado cuando está haciendo eso? Procesador de formularios.

¿Quién le da al cliente el widget? El empleado que actúa como Distribuidor de Widget

Y así sucesivamente ...

Para insertar esto en una implementación de Java EE ...

El Servlet actúa en nombre del Cliente, llena el formulario (extrae datos de la solicitud HTTP y crea el objeto Java apropiado) y se lo pasa al empleado apropiado (EJB), quien luego hace con el formulario lo que necesita para acabar. Mientras procesa la solicitud, es posible que el EJB deba pasarlo a otro EJB que se especialice en diferentes tareas, parte de las cuales incluiría acceder / poner información desde / hacia el almacenamiento (su capa de datos). Lo único que no debe asignarse directamente a la analogía debe ser la información específica sobre cómo se comunican los objetos entre sí y cómo la capa de datos se comunica con su almacenamiento.

Yo mismo he tenido los mismos pensamientos.

En el MVC tradicional, lo más importante es separar la Vista de las partes del Modelo y del Controlador. Parece ser una buena idea separar el controlador y el modelo simplemente porque puede terminar con objetos de modelo hinchados:


public class UserModel extends DatabaseModel implements XMLable, CRUDdy, Serializable, Fooable, Barloney, Baznatchy, Wibbling, Validating {
  // member fields
  // getters and setters
  // 100 interface methods
}

Mientras que puedes tener controladores separados (o patrones completos) para muchas de las interfaces anteriores, y sí, es bastante de naturaleza procesal pero supongo que así es como funcionan las cosas en estos días. Alternativamente, puede darse cuenta de que algunas de las interfaces están haciendo lo mismo (CRUDdy: almacenamiento y recuperación de la base de datos, serializable, igual a un formato binario, XMLable, igual a XML), por lo que debe crear un sistema único para manejar esto, con cada backend potencial es una implementación separada que el sistema maneja. Dios, eso está realmente mal escrito.

Tal vez haya algo así como " co-clases " que le permiten tener archivos de origen separados para la implementación del controlador que actúan como si fueran miembros de la clase modelo en la que actúan.

En cuanto a las reglas de negocios, a menudo funcionan en varios modelos al mismo tiempo y, por lo tanto, deben estar separados.

Creo que esta es una pregunta sobre " separación de preocupaciones " ;. Parece que estás muy lejos en el camino correcto con tu arquitectura en capas, pero tal vez necesites hacer más de lo mismo, es decir, ¿crear capas arquitectónicas dentro de tus capas Java EE?

Un DataContainer se parece mucho al patrón de Objetos de Transferencia de Datos (DTO).

Un diseño moderno de OO tiene muchas clases pequeñas, cada una relacionada con un pequeño número de "amigos", por ejemplo. a través de la composición. Esto podría producir muchas más clases y placa de caldera de Java de lo que realmente te sientes cómodo, pero debería llevar a un diseño que sea mejor en capas y más fácil de probar y mantener por unidad.

(+1 para la pregunta, +1 para la respuesta sobre cuándo sabes que tienes las capas correctas)

Crudamente, el " No queremos " La sección tiene que ir. O quieres que sea lo correcto, o quieres que se quede como está. No tiene sentido que no cree una clase cuando la necesite. " Tenemos un montón " Es una mala excusa.

De hecho, es malo exponer todas las clases internas, pero en mi experiencia, crear una clase por concepto (es decir, un usuario, una identificación, un concepto de base de datos). ...) siempre ayuda.

Además, ¿no es un patrón de fachada algo que resuelva la existencia de cargas de clases de BizRules, oculto detrás de una interfaz limpia y bien organizada?

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