Рыдая от радости, я наконец-то закончил чертову если-иначе

Java
Рыдая от радости, я наконец-то закончил чертову если-иначе

Я разобрал кое-какую архитектуру Java и материалы интервью (микросервисы, кластеры, распределенное, промежуточное ПО и т.д.), а друзья, кому нужно, могут обратить внимание на официальный аккаунт [Внутренние дела Программиста], и получить самостоятельно без всяких рутин

Отличный обзор:


Во-первых, традиционный способ реализации

Давайте сначала поговорим о конкретных потребностях: у компании много порталов продвижения, и каждый источник заказа обрабатывается со специальной логикой при оформлении заказа, и источник может добавляться каждые два дня.

Тогда код по традиционному способу реализации выглядит следующим образом:

public class OrderServiceImpl implements IOrderService {
    @Override
    public String handle(OrderDTO dto) {
        String type = dto.getType();
        if ("1".equals(type)) {
            return "处理普通订单";
        } else if ("2".equals(type)) {
            return "处理团购订单";
        } else if ("3".equals(type)) {
            return "处理促销订单";
        }
        return null;
    }
}

Почему он должен быть таким раздутым? Многие коллеги скажут: «Ой, никак, бизнес тугой, поэтому оперативность разработки быстрая и легкая». Это правда, что во многих случаях деловая сторона действительно заставляет вас догонять период строительства, как призрак, и вы хотите быстро реализовать функцию, это лучший выбор.

Код выше вроде бы достаточно понятен, но если я вам скажу, что в компании сотни источников заказов, представьте себе такое раздутое if-else, что вы почувствуете, пролистывая код?

Во-вторых, реализация шаблона стратегии

Шаблон стратегииoopОдним из наиболее известных шаблонов проектирования в , является абстракция поведения метода, которую можно классифицировать как поведенческий дизайн.oopсерединаinterfaceКлассическое приложение. Его функции просты и функциональны, и это один из моих любимых режимов.

Шаблон стратегии определяет семейство алгоритмов с общим поведением, каждый из которых инкапсулирован, может заменяться друг другом и изменяется независимо от клиента.

Многие люди говорят: шаблоны проектирования Java были пересказаны много раз, но это не дело написания if-else в повседневной жизни, и оно вообще не используется. На самом деле дело не в том, что он не используется или используется не в том месте!

1. Используйте сценарии режима стратегии:
  • Существует несколько способов решения одной и той же проблемы, только когда конкретное поведение отличается;
  • Когда необходимо безопасно инкапсулировать несколько операций одного типа;
  • Когда существует несколько подклассов одного и того же абстрактного класса, и клиенту необходимо использовать if-else или switch-case для выбора конкретного подкласса.

Это модифицированный код, использующий режим стратегии:

@Component
@OrderHandlerType(16)
public class DispatchModeProcessor extends AbstractHandler{

	@Autowired
	private OrderStencilledService orderStencilledService;
	
	@Override
	public void handle(OrderBO orderBO) {
		
		/**
    	 * 订单完结广播通知(1 - 支付完成)
    	 */
    	orderStencilledService.dispatchModeFanout(orderBO);
		
    	/**
    	 *  SCMS 出库单
    	 */
    	orderStencilledService.createScmsDeliveryOrder(orderBO.getPayOrderInfoBO().getLocalOrderNo());
	}
}

Каждый источник заказа имеет свой отдельный класс реализации логики, и каждый раз, когда вам нужно добавить источник заказа, создайте новый класс реализации напрямую, измените@OrderHandlerType(16)Значения достаточно, и вам не нужно раз и навсегда проходить через сотни строк!

2. Конкретный процесс реализации:

1. Определите аннотацию, которая идентифицирует источник заказа

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OrderHandlerType {
	int value() default 0;
}

2. Абстрагируйте конкретный бизнес-процессор

public abstract class AbstractHandler {
	abstract public void handle(OrderBO orderBO);
}

3. Сканирование запуска проектаhandlerВход

@Component
@SuppressWarnings({"unused","rawtypes"})
public class HandlerProcessor implements BeanFactoryPostProcessor {
	
	private String basePackage = "com.ecej.order.pipeline.processor";
	
    public static final Logger log = LoggerFactory.getLogger(HandlerProcessor.class);
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
		Map<Integer,Class> map = new HashMap<Integer,Class>();
		
		ClassScaner.scan(basePackage, OrderHandlerType.class).forEach(x ->{
			int type = x.getAnnotation(OrderHandlerType.class).value();
			map.put(type,x);
		});
		
		beanFactory.registerSingleton(OrderHandlerType.class.getName(), map);
		
		log.info("处理器初始化{}", JSONObject.toJSONString(beanFactory.getBean(OrderHandlerType.class.getName())));
	}
}

4. Инструменты, необходимые для сканирования

public class ClassScaner {
	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	private final List<TypeFilter> includeFilters = new ArrayList<TypeFilter>();

	private final List<TypeFilter> excludeFilters = new ArrayList<TypeFilter>();

	private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
	
	/**
	 * 添加包含的Fiter
	 * @param includeFilter
	 */
	public void addIncludeFilter(TypeFilter includeFilter) {
		this.includeFilters.add(includeFilter);
	}

	/**
	 * 添加排除的Fiter
	 * @param includeFilter
	 */
	public void addExcludeFilter(TypeFilter excludeFilter) {
		this.excludeFilters.add(excludeFilter);
	}
	
	/**
	 * 扫描指定的包,获取包下所有的Class
	 * @param basePackage 包名
	 * @param targetTypes 需要指定的目标类型,可以是pojo,可以是注解
	 * @return Set<Class<?>>
	 */
	public static Set<Class<?>> scan(String basePackage,
			Class<?>... targetTypes) {
		ClassScaner cs = new ClassScaner();
		for (Class<?> targetType : targetTypes){
			if(TypeUtils.isAssignable(Annotation.class, targetType)){
				cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
			}else{
				cs.addIncludeFilter(new AssignableTypeFilter(targetType));
			}
		}
		return cs.doScan(basePackage);
	}
	
	/**
	 * 扫描指定的包,获取包下所有的Class
	 * @param basePackages 包名,多个
	 * @param targetTypes 需要指定的目标类型,可以是pojo,可以是注解
	 * @return Set<Class<?>>
	 */
	public static Set<Class<?>> scan(String[] basePackages,
			Class<?>... targetTypes) {
		ClassScaner cs = new ClassScaner();
		for (Class<?> targetType : targetTypes){
			if(TypeUtils.isAssignable(Annotation.class, targetType)){
				cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
			}else{
				cs.addIncludeFilter(new AssignableTypeFilter(targetType));
			}
		}
		Set<Class<?>> classes = new HashSet<Class<?>>();
		for (String s : basePackages){
			classes.addAll(cs.doScan(s));
		}
		return classes;
	}
	
	/**
	 * 扫描指定的包,获取包下所有的Class
	 * @param basePackages 包名
	 * @return Set<Class<?>>
	 */
	public Set<Class<?>> doScan(String [] basePackages) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		for (String basePackage :basePackages) {
			classes.addAll(doScan(basePackage));
		}
		return classes;
	}
	
	/**
	 * 扫描指定的包,获取包下所有的Class
	 * @param basePackages 包名
	 * @return Set<Class<?>>
	 */
	public Set<Class<?>> doScan(String basePackage) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
					+ ClassUtils.convertClassNameToResourcePath(
							SystemPropertyUtils.resolvePlaceholders(basePackage))+"/**/*.class";
			Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
			for (int i = 0; i < resources.length; i++) {
				Resource resource = resources[i];
				if (resource.isReadable()) {
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
					if ((includeFilters.size() == 0 && excludeFilters.size() == 0)|| matches(metadataReader)) {
						try {
							classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
						} catch (ClassNotFoundException ignore) {}
					}
				}
			}
		} catch (IOException ex) {
			throw new RuntimeException("I/O failure during classpath scanning", ex);
		}
		return classes;
	}
	
	/**
	 * 处理 excludeFilters和includeFilters
	 * @param metadataReader
	 * @return boolean
	 * @throws IOException
	 */
	private boolean matches(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return true;
			}
		}
		return false;
	}
}

5. Создайте абстрактные классы в соответствии с типами


@Component
public class HandlerContext {

	@Autowired
	private ApplicationContext beanFactory;

	public  AbstractHandler getInstance(Integer type){
		
		Map<Integer,Class> map = (Map<Integer, Class>) beanFactory.getBean(OrderHandlerType.class.getName());
		
		return (AbstractHandler)beanFactory.getBean(map.get(type));
	}
	
}

6. Позвоните в подъезд, вот полученное мной сообщение MQ, которое будет обрабатывать несколько источников заказов в пакетах

@Component
@RabbitListener(queues = "OrderPipelineQueue")
public class PipelineSubscribe{
 
	private final Logger LOGGER = LoggerFactory.getLogger(PipelineSubscribe.class);
	
	@Autowired
	private HandlerContext HandlerContext;
	
	@Autowired
	private OrderValidateService orderValidateService;
	
    @RabbitHandler
    public void subscribeMessage(MessageBean bean){
    	
    	OrderBO orderBO = JSONObject.parseObject(bean.getOrderBO(), OrderBO.class);
    	
    	if(null != orderBO &&CollectionUtils.isNotEmpty(bean.getType()))
    	{
    		for(int value:bean.getType())
    		{
       		 AbstractHandler handler = HandlerContext.getInstance(value);
       		 handler.handle(orderBO);
    		}
		}
	}
}

принимающая организацияMessageBeanкод класса

public class MessageBean implements Serializable {
    private static final long serialVersionUID = 5454831432308782668L;
    private String cachKey;
    private List<Integer> type;
    private String orderBO;

    public MessageBean(List<Integer> type, String orderBO) {
        this.type = type;
        this.orderBO = orderBO;
    }
}

Преимущества и недостатки режима стратегии

преимущество
  • Легко расширяется, добавление новой стратегии требует только добавления определенного класса стратегии, в основном не нужно изменять исходный код в соответствии с принципом открытого и закрытого
  • Избегайте использования нескольких операторов условного выбора, чтобы полностью отразить идею объектно-ориентированного проектирования. Классы стратегии можно свободно переключать. Поскольку все классы стратегии реализуют один и тот же интерфейс, их можно свободно переключать между собой.
  • Каждый класс политики использует один класс политики, который соответствует принципу единой ответственности.Клиент отделен от алгоритма политики, и оба полагаются на абстрактный интерфейс политики, который соответствует принципу инверсии зависимостей.
  • Клиенту не нужно знать, какие классы политик доступны, что соответствует принципу минимальных знаний.
недостаток
  • Режим стратегии, когда слишком много алгоритмов стратегии, это вызовет много классов стратегии
  • Клиент не знает, какие существуют классы политик, и не может решить, какой класс политик использовать.Это можно решить, инкапсулировав общий общедоступный пакет или рассмотрев возможность использованияIOC容器и依赖注入способ решить.

Ниже приведена часть класса стратегии источника ордера.Надо сказать, что классов стратегий действительно много.

在这里插入图片描述

Суммировать:

У всего есть две стороны,if-elseНесколько уровней вложенности также имеют свои преимущества и недостатки:

if-elseПреимущество этого в том, что это просто, если вы хотите быстро перебирать функции, вложенность логики меньше и больше не будет увеличиваться.if-elseБолее того, недостатки тоже очевидны, код раздут, громоздок и не прост в сопровождении.

策略模式Отделите логику каждой сцены для обслуживания.Один и тот же абстрактный класс имеет несколько подклассов, которые необходимо использовать.if-elseилиswitch-caseПри выборе конкретного подкласса рекомендуется выбрать режим стратегии, его недостатком является то, что он будет генерировать больше файлов классов стратегий.

Два метода реализации имеют свои преимущества и недостатки.Как выбрать, зависит от конкретного бизнес-сценария или от того, что режим построения предложений не используется для использования и должен использоваться в наиболее подходящей позиции.


Сегодня так много нужно сказать, если эта статья была вам полезна, я надеюсь получить от вас лайк 👍

Ваше одобрение является движущей силой для моего письма!


Небольшие преимущества:

Сотни различных технических электронных книг пересылаются друг другу, тсс~,бесплатноОтправить его друзьям. Ответ публичного аккаунта【666] Самовывоз

Я разобрал кое-какую архитектуру Java и материалы интервью, а нуждающиеся друзья могут обратить внимание на паблик-аккаунт [Внутри программатора