Usa el modo de estrategia y el modo de fábrica para matar if-else

Para el desarrollo empresarial, la complejidad de la lógica empresarial es inevitable. A medida que se desarrolla el negocio, los requisitos se volverán cada vez más complejos. Para considerar diversas situaciones, es inevitable que haya muchos if-else en el código.

Una vez que haya demasiados if-else en el código, afectará en gran medida su legibilidad y mantenibilidad.

En primer lugar, legibilidad. No hace falta decir que demasiado código if-else y anidamiento pueden dificultar que las personas que lean el código comprendan lo que significa. Especialmente el código que no se comenta.

El segundo es la capacidad de mantenimiento, debido a que hay tantos if-else, cuando desee agregar una nueva rama, será difícil de agregar y es extremadamente fácil afectar a otras ramas.

El autor vio una vez una aplicación de pago central. Esta aplicación es compatible con las funciones de pago en línea de muchas empresas, pero cada empresa tiene muchos requisitos de personalización, por lo que hay muchos if-else en muchos códigos básicos.

Cuando sea necesario personalizar cada nuevo negocio, coloque el suyo propio en la parte superior de todo el método para asegurarse de que su lógica se pueda ejecutar con normalidad. Se pueden imaginar las consecuencias de este enfoque.

De hecho, hay formas de eliminar if-else. La más típica y ampliamente utilizada es utilizar el modo de estrategia y el modo de fábrica. Para ser precisos, utilice las ideas de estos dos modos de diseño para eliminar completamente el if-else en el código.

Este artículo combina estos dos patrones de diseño, presenta cómo eliminar if-else y también cómo combinar con el marco Spring, para que los lectores puedan postularse inmediatamente a sus propios proyectos después de leer este artículo.

Este artículo incluye algo de código, pero el autor hace todo lo posible por utilizar ejemplos populares y pseudocódigo para que el contenido sea menos aburrido.

Asqueroso si-si no

Supongamos que queremos hacer una plataforma de entrega de alimentos y tenemos tal demanda:

1. Cierta tienda en la plataforma de comida para llevar ha establecido una variedad de descuentos para miembros para la promoción, incluido un descuento del 20% para los supermiembros, un descuento del 10% para los miembros normales y ningún descuento para los usuarios normales.

2. Se espera que cuando el usuario realiza un pago, según el nivel de membresía del usuario, pueda saber con qué estrategia de descuento cumple el usuario, para luego realizar descuentos y calcular el monto a pagar.

3. Con el desarrollo del negocio, las nuevas demandas requieren que los miembros exclusivos disfruten del descuento solo cuando el monto del pedido sea superior a 30 yuanes en la tienda.

4. Luego, hay otra demanda anormal. Si el super miembro del usuario ha expirado y el tiempo de vencimiento es dentro de una semana, entonces el pedido único del usuario se descontará de acuerdo con el super miembro, y se hará un fuerte recordatorio en el cajero. ., Para guiar a los usuarios a reabrir la membresía, y el descuento solo se realiza una vez.

Entonces, podemos ver el siguiente pseudocódigo:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {
    
    
 
    if (用户是专属会员) {
    
    
        if (订单金额大于30) {
    
    
            returen 7折价格;
        }
    }
 
    if (用户是超级会员) {
    
    
        return 8折价格;
    }
 
    if (用户是普通会员) {
    
    
        if(该用户超级会员刚过期并且尚未使用过临时折扣){
    
    
            临时折扣使用次数更新();
            returen 8折价格;
        }
        return 9折价格;
    }
    return 原价;
}

Lo anterior es una pieza de la lógica de cálculo de precios para esta demanda. El uso de pseudocódigo es tan complicado. Si el código está realmente escrito, la complejidad se puede imaginar.

En dicho código, hay muchos if-else, y hay muchos anidamientos if-else, tanto la legibilidad como la mantenibilidad son muy bajas.

Entonces, ¿cómo se puede mejorar?

Modo de estrategia

A continuación, intentamos introducir un patrón de estrategia para mejorar la facilidad de mantenimiento y la legibilidad del código.

Primero, defina una interfaz:

/**
 * @author springRoot
 */
public interface UserPayService {
    
    
 
    /**
     * 计算应付价格
     */
    public BigDecimal quote(BigDecimal orderPrice);
}

Luego defina varias clases de estrategias:

/**
 * @author springRoot
 */
public class ParticularlyVipPayService implements UserPayService {
    
    
 
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
    
    
         if (消费金额大于30) {
    
    
            return 7折价格;
        }
    }
}
 
public class SuperVipPayService implements UserPayService {
    
    
 
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
    
    
        return 8折价格;
    }
}
 
public class VipPayService implements UserPayService {
    
    
 
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
    
    
        if(该用户超级会员刚过期并且尚未使用过临时折扣){
    
    
            临时折扣使用次数更新();
            returen 8折价格;
        }
        return 9折价格;
    }
}

Después de introducir la estrategia, podemos calcular el precio de la siguiente manera:

/**
 * @author springRoot
 */
public class Test {
    
    
 
    public static void main(String[] args) {
    
    
        UserPayService strategy = new VipPayService();
        BigDecimal quote = strategy.quote(300);
        System.out.println("普通会员商品的最终价格为:" + quote.doubleValue());
 
        strategy = new SuperVipPayService();
        quote = strategy.quote(300);
        System.out.println("超级会员商品的最终价格为:" + quote.doubleValue());
    }
}

Lo anterior es un ejemplo. Puede crear diferentes clases de estrategias de miembros en el código y luego ejecutar el método correspondiente para calcular el precio. Para este ejemplo y el conocimiento relacionado del modo de estrategia, los lectores pueden leer en " ¿Cómo explicarle a una novia qué es un modo de estrategia?" "Aprendiendo en un artículo.

Sin embargo, si realmente se usa en código, como en un proyecto web, la demostración anterior no se puede usar directamente.

En primer lugar, en el proyecto web, las clases de estrategia que creamos anteriormente son administradas por Spring y no crearemos una nueva instancia por nosotros mismos.

En segundo lugar, en proyectos web, si realmente quieres calcular el precio, también debes conocer de antemano el nivel de membresía del usuario, como averiguar el nivel de membresía de la base de datos, y luego obtener diferentes estrategias basadas en el nivel para ejecutar el precio. método de cálculo.

Luego, para el cálculo del precio real en un proyecto web, el pseudocódigo debería verse así:

/**
 * @author springRoot
 */
public BigDecimal calPrice(BigDecimal orderPrice,User user) {
    
    
 
     String vipType = user.getVipType();
 
     if (vipType == 专属会员) {
    
    
        //伪代码:从Spring中获取超级会员的策略对象
        UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class);
        return strategy.quote(orderPrice);
     }
 
     if (vipType == 超级会员) {
    
    
        UserPayService strategy = Spring.getBean(SuperVipPayService.class);
        return strategy.quote(orderPrice);
     }
 
     if (vipType == 普通会员) {
    
    
        UserPayService strategy = Spring.getBean(VipPayService.class);
        return strategy.quote(orderPrice);
     }
     return 原价;
}

A través del código anterior, encontramos que la facilidad de mantenimiento y la legibilidad del código parecen ser mejores, pero no parece reducir if-else.

De hecho, en el anterior " ¿Cómo explicarle a una novia qué es un modelo estratégico?" En el artículo, presentamos muchas ventajas del modelo de estrategia. Sin embargo, todavía existe una gran desventaja en el uso del modo de estrategia:

El cliente debe conocer todas las clases de estrategia y decidir qué clase de estrategia utilizar. Esto significa que el cliente debe comprender la diferencia entre estos algoritmos para poder seleccionar la clase de algoritmo adecuada a tiempo.

En otras palabras, aunque no hay un if-else al calcular el precio, es inevitable tener un if-else al momento de elegir una estrategia específica.

Además, en el pseudocódigo anterior, implementamos el pseudocódigo para obtener el objeto de política del miembro de Spring. Entonces, ¿cómo obtiene el código el Bean correspondiente?

A continuación, veamos cómo resolver estos problemas con la ayuda de Spring y el patrón de fábrica.

Modo de fábrica

Para facilitarnos la obtención de las diversas clases de políticas de UserPayService de Spring, creamos una clase de fábrica:

/**
 * @author springRoot
 */
public class UserPayServiceStrategyFactory {
    
    
 
    private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();
 
    public  static UserPayService getByUserType(String type){
    
    
        return services.get(type);
    }
 
    public static void register(String userType,UserPayService userPayService){
    
    
        Assert.notNull(userType,"userType can't be null");
        services.put(userType,userPayService);
    }
}

Este UserPayServiceStrategyFactory define un mapa, que se utiliza para almacenar todas las instancias de la clase de estrategia, y proporciona un método getByUserType, que puede obtener directamente la instancia de clase correspondiente según el tipo. También existe un método de registro, que se discutirá más adelante.

Con esta clase de fábrica, el código para calcular el precio se puede optimizar en gran medida:

/**
 * @author springRoot
 */
public BigDecimal calPrice(BigDecimal orderPrice,User user) {
    
    
 
     String vipType = user.getVipType();
     UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType);
     return strategy.quote(orderPrice);
}

En el código anterior, if-else ya no es necesario. Después de obtener el tipo de VIP del usuario, se puede llamar directamente a través del método getByUserType de la fábrica.

A través de Strategy + Factory, nuestro código se ha optimizado en gran medida, mejorando enormemente la legibilidad y la mantenibilidad.

Sin embargo, todavía queda un problema de lo anterior, es decir, ¿cómo se usa el mapa para almacenar todas las instancias de la clase de estrategia en el UserPayServiceStrategyFactory inicializado? ¿Cómo se introducen los objetos de instancia de cada estrategia?

Registro de Spring Bean

¿Recuerda el método de registro proporcionado en UserPayServiceStrategyFactory que definimos anteriormente? Se utiliza para registrar servicios de pólizas.

A continuación, encontraremos una forma de llamar al método register y registrar el Bean creado por Spring a través de IOC.

Para este tipo de demanda, puede tomar prestada la interfaz InitializingBean proporcionada por Spring. Esta interfaz proporciona un método de procesamiento para el Bean después de la inicialización de la propiedad. Solo incluye el método afterPropertiesSet. Todas las clases que hereden esta interfaz ejecutarán este método después de las propiedades del bean se inicializan.

Luego, podemos modificar ligeramente las clases de estrategia anteriores:

/**
 * @author springRoot
 */
@Service
public class ParticularlyVipPayService implements UserPayService,InitializingBean {
    
    
 
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
    
    
         if (消费金额大于30) {
    
    
            return 7折价格;
        }
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        UserPayServiceStrategyFactory.register("ParticularlyVip",this);
    }
}
 
@Service
public class SuperVipPayService implements UserPayService ,InitializingBean{
    
    
 
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
    
    
        return 8折价格;
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        UserPayServiceStrategyFactory.register("SuperVip",this);
    }
}
 
@Service  
public class VipPayService implements UserPayService,InitializingBean {
    
    
 
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
    
    
        if(该用户超级会员刚过期并且尚未使用过临时折扣){
    
    
            临时折扣使用次数更新();
            returen 8折价格;
        }
        return 9折价格;
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        UserPayServiceStrategyFactory.register("Vip",this);
    }
}

Solo necesita implementar la interfaz InitializingBean para cada clase de implementación de servicio de estrategia, implementar su método afterPropertiesSet y llamar a UserPayServiceStrategyFactory.register en este método.

De esta manera, cuando se inicializa Spring, cuando se crean VipPayService, SuperVipPayService y ParticularlyVipPayService, el Bean se registrará en UserPayServiceStrategyFactory después de que se inicialicen las propiedades del Bean.

El código anterior en realidad tiene algo de código repetitivo, que también se puede simplificar aún más introduciendo el patrón del método de plantilla, que no se expandirá aquí.

Además, cuando se llama a UserPayServiceStrategyFactory.register, se debe pasar una cadena al primer parámetro, que en realidad se puede optimizar aquí. Por ejemplo, utilice la enumeración o personalice un método getUserType en cada clase de estrategia e impleméntelos por separado.

para resumir

En este artículo, hemos mejorado la legibilidad y el mantenimiento del código a través del modo de estrategia, el modo de fábrica y el InitializingBean de Spring, y hemos eliminado por completo un montón de if-else.

 

Supongo que te gusta

Origin blog.csdn.net/weixin_43743650/article/details/103645148
Recomendado
Clasificación