Во многих случаях в нашем коде много ветвей, а код под ветвью имеет сложную логику, и я думаю, что многим нравится использовать if-else/switch-case для ее реализации. Плохие поместят код реализации непосредственно в ветку if-else/switch-case:
switch ( type ) {
case case1:
...
...
break;
case case2:
...
...
break;
case case3:
...
...
break
default:
return null;
}
Код типа не только многословен, но и очень труден для чтения. Лучший способ сделать это — инкапсулировать эту логику в функции и вызывать их в ветке:
switch ( type ) {
case case1:
return case1Func();
case case2:
return case2Func();
case case3:
return case3Func();
default:
return null;
}
Даже это процессно-ориентированный способ написания.В прошлом при написании программ на C мне всегда нравилось писать таким образом, и здесь вообще нет никакого шаблона проектирования. Мало того, что это нарушает принцип открытия-закрытия, так еще и по мере увеличения количества веток switch-case код будет становиться только более многословным. На самом деле, в этом коде уже есть зрелые шаблоны для устранения многих ветвей if-else/switch-case. Эта статья научит вас, как устранить if-else/switch-case в Spring с помощью аннотации + шаблона стратегии + простой фабрики. Возьмем в качестве примера личный центр пространства QQ.Если в личном центре пространства QQ есть четыре вкладки, то на них перечислены мои выступления, мой журнал, мои фотографии и мои посетители. Общий фоновый код, вероятно, будет следующим:
//各个 tab 名称的枚举:
public enum UserRelatedType {
/**
* 说说
*/
SHUOSHUO("说说"),
/**
* 日志
*/
RIZHI("日志"),
/**
* 发布
*/
ZHAOPIAN("照片"),
/**
* 访客
*/
FANGKE("");
private String desc;
UserRelatedType(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
Перечислите код вкладки, связанной с личным центром пользователя QQ:
public List<UserRelatedVO> listRelated(UserRelatedQuery query){
UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) );
switch ( relatedType ) {
case SHUOSHUO:
return listRelatedShuoshuo( query );
case RIZHI:
return listRelatedRizhi( query );
case ZHAOPIAN:
return listRelatedZhaopian( query );
case FANGKE:
return listRelatedFangke( query );
default:
return null;
}
}
И используйте аннотацию + режим стратегии + простую фабрику, рефакторинг кода выглядит следующим образом:
1. Определите аннотацию, чтобы полностью исключить if-else:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RelatedTypeAnnotation {
/**
* 用户相关类型名称
*/
UserRelatedType value();
}
2. Сначала определите интерфейс, все вкладки должны реализовывать этот интерфейс. Где list — способ отображения данных вкладки.
public interface UserRelated {
/**
* 列出详细信息
*
* @param query
* @return
*/
List<UserRelatedVO> list(UserRelatedQuery query);
}
3. Определите реализацию конкретных вкладок и наследуйте интерфейс стратегии UserRelated.
- мой разговор
@Component("userRelatedShuoshuo")
@RelatedTypeAnnotation( value = UserRelatedType.SHUOSHUO )
public class UserRelatedShuoshuo implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的说说!");
return list;
}
}
- мой журнал
@Component("userRelatedRizhi")
@RelatedTypeAnnotation( value = UserRelatedType.RIZHI )
public class UserRelatedRizhi implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的日志!");
return list;
}
}
- Мое фото
@Component("userRelatedZhaopian")
@RelatedTypeAnnotation( value = UserRelatedType.ZHAOPIAN )
public class UserRelatedZhaopian implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的照片!");
return list;
}
}
- мой посетитель
@Component("userRelatedFangke")
@RelatedTypeAnnotation( value = UserRelatedType.FANGKE )
public class UserRelatedFangke implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的访客!");
return list;
}
}
3. Определите класс инструмента, который получает bean-компоненты из контекста Spring.
@Component
public class SpringContextUtil implements ApplicationContextAware {
private ApplicationContext context;
public ApplicationContext getContext() {
return context;
}
@Override
public void setApplicationContext(ApplicationContext context)throws BeansException {
this.context = context;
}
}
4. Определите простую фабрику для производства различных объектов вкладок.
@Component
public class UserRelatedFactory {
@Autowired
SpringContextUtil springContextUtil;
private static Map<UserRelatedType, UserRelated> userRelatedMap = Maps.newConcurrentMap();
//工厂将 Spring 装配的相关的 Bean 用 Map 保存起来
@PostConstruct
public void init{
Map<String, Object> beanMap = springContextUtil.getContext().getBeansWithAnnotation(RelatedTypeAnnotation.class);
for(Object userRelated : beanMap.values()) {
RelatedTypeAnnotation annotation = userRelated.getClass().getAnnotation(RelatedTypeAnnotation.class);
userRelatedMap.put(annotation.value(), (UserRelated)userRelated);
}
}
public static UserRelated createRelated(UserRelatedType relatedType) {
return userRelatedMap.get( relatedType );
}
}
5. Вызываемый код (в контроллере будет вызываться listRelated).
public List<UserRelatedVO> listRelated(UserRelatedQuery query){
UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) );
UserRelated related = UserRelatedFactory.createRelated( relatedType );
if( related != null ) {
return related.list( query );
} else {
return null;
}
}
Если вам нужно добавить еще одну вкладку в рефакторинговый код, например мой друг, вам нужно только добавить тип, который наследует UserRelated для реализации в нем списка, и добавить соответствующие аннотации.
На самом деле это общее решение, и вы должны учитывать этот шаблон, когда ваш if-else/switch-case имеет более 3 ветвей, а код ветвления похож и многословен. Код, написанный в этом режиме, является объектно-ориентированным, понятным, легко расширяемым и длинным, так что почему бы не сделать это, попробуйте прямо сейчас! Не забудьте обратить внимание на официальный аккаунт, в котором записан путь обучения программиста C++ Java.