¿Cómo debo refactorizar las declaraciones de cambio como esta (Encender el tipo) para que sean más OO?

7

Estoy viendo un código como este en nuestra base de código, y quiero refactorizarlo:

(Sigue el código de código de typScript):

class EntityManager{

private findEntityForServerObject(entityType:string, serverObject:any):IEntity {

    var existingEntity:IEntity = null;

    switch(entityType) {
      case Types.UserSetting:
            existingEntity = this.getUserSettingByUserIdAndSettingName(serverObject.user_id, serverObject.setting_name);
                break;

        case Types.Bar:
            existingEntity = this.getBarByUserIdAndId(serverObject.user_id, serverObject.id);
            break;

        //Lots more case statements here...
    }
    return existingEntity;
 }

 }

Las desventajas de la activación del tipo son autoexplicativas. Normalmente, al cambiar el comportamiento según el tipo, trato de incluir el comportamiento en subclases para poder reducirlo a una sola llamada de método y dejar que el polimorfismo se ocupe del resto.

Sin embargo, las siguientes dos cosas me están dando pausa:

1) No quiero unir el serverObject con la clase que almacena todos estos objetos. No sabe dónde buscar entidades de un determinado tipo. Y, desafortunadamente, la identidad de un tipo de ServerObject varía con el tipo de ServerObject. (Así que a veces es solo una identificación, otras veces es una combinación de una identificación y una cadena de identificación única, etc.) Y este comportamiento no pertenece a esas subclases. Es responsabilidad del EntityManager y sus delegados.

2) En este caso, no puedo modificar las clases de ServerObject ya que son objetos de datos antiguos. Debe mencionarse que tengo otras instancias del método anterior que toman un parámetro como "IEntity" y proceden a hacer casi lo mismo (pero modifican ligeramente el nombre de los métodos que están llamando para obtener la identidad del entidad). Por lo tanto, podríamos tener:

        case Types.Bar:
            existingEntity = this.getBarByUserIdAndId(entity.getUserId(), entity.getId());
            break;

Entonces, en ese caso, puedo cambiar la interfaz de la entidad y las subclases, pero esto no es un comportamiento que pertenece a esa clase.

Por lo tanto, creo que me apunta a algún tipo de mapa. Así que eventualmente llamaré:

private findEntityForServerObject(entityType:string, serverObject:any):IEntity {

    return aMapOfSomeSort[entityType].findByServerObject(serverObject);

}

private findEntityForEntity(someEntity:IEntity):IEntity {
    return aMapOfSomeSort[someEntity.entityType].findByEntity(someEntity);
}

Lo que significa que necesito registrar algún tipo de clases / funciones de estrategia en tiempo de ejecución con este mapa. Y una vez más, sería mejor que recuerde registrar uno para cada uno de mis tipos o obtendré una excepción de tiempo de ejecución.

¿Hay una mejor manera de refactorizar esto? Siento que me estoy perdiendo algo realmente obvio aquí.

    
pregunta Taytay 03.06.2014 - 18:22

2 respuestas

2

Cada vez que empieces a cambiar por tipo (o proporcionar un tipo basado en el tipo), vas contra el modelo OO. En un buen diseño de OO, la clase de implementación debe estar oculta del código de llamada.

(Dicho esto, hay veces en que es más fácil hacer una instancia rápida, pero trato de hacerlo raro).

El polimorfismo es la respuesta aquí.

Realmente, lo que estás haciendo es llamar a alguna acción común en algunas clases. Entonces, apague una interfaz que defina esa llamada común y luego haga referencia a los objetos a través de esa interfaz.

En otras palabras, cada vez que me veo escribiendo "if xxx.class" o "xxx instanceof" o "switch (xxx.class)", me pregunto si hay una interfaz común que pueda extraer.

Ahora, puede ser complicado. Si las diferentes clases requieren diferentes parámetros, es posible que necesite usar algún tipo de patrón de Generador. Puede complicarse.

De todos modos, la respuesta corta es: si está cambiando o si está basado en un tipo de clase, debería usar el polimorfismo en su lugar.

    
respondido por el Rob 03.06.2014 - 19:18
1

Yo mapearía entre entityTypes y un objeto que sabe qué hacer con ese tipo de entidad. Así que puedes tener algo como

private findEntityForServerObject(entityType:string, serverObject:any):IEntity {
    EntityFinder entityFinder = EntityFinder.getForEntity(entityType);
    return entityFinder.find(serverObject);
}

Luego, presionará el interruptor en EntityFinder, que tendrá un método estático en las siguientes líneas

class EntityFinder {
    public static EntityFinder getForEntity(String entityType) {
        switch (entityType) {
            case Types.UserSetting:
               return new UserSettingsFinder();
            case Types.Bar:
               return new BarSettingsFinder();
            default:
               // Possibly better than a null pointer
               return new UnknownSettingsFinder(); 
        }
    }
}

Si introduce un nuevo tipo, tendrá que acordarse de incluirlo en el conmutador pero, con suerte, solo tendrá un lugar para hacerlo.

    
respondido por el PhilDin 03.06.2014 - 19:03

Lea otras preguntas en las etiquetas