Java 設計模式--策略模式,枚舉+工廠方法實現


如果項目中的一個頁面跳轉功能存在10個以上的if else判斷,想要做一下整改

一、什么是策略模式

策略模式是對算法的包裝,是把使用算法的責任和算法本身分割開來,委派給不同的對象管理,最終可以實現解決多重If判斷問題。

1.環境(Context)角色:持有一個Strategy的引用。

2.抽象策略(Strategy)角色:這是一個抽象角色,通常由一個接口或抽象類實現。此角色給出所有的具體策略類所需的接口。

3.具體策略(ConcreteStrategy)角色:包裝了相關的算法或行為。

(定義策略接口→實現不同的策略類→利用多態或其他方式調用策略。)

二、策略模式優缺點

優點:

算法可以自由切換(高層屏蔽算法,角色自由切換)

避免使用多重條件判斷(如果算法過多就會出現很多相同的判斷,很難維護)

擴展性好(可自由添加取消算法,而不影響整個功能)

缺點:

策略數量增多(每一個策略類復用性小,如果需要增加算法,就只能新增類)

所有的策略類都需要對外暴露(使用的人必須了解使用策略,這個就需要其他模式來補充,比如工廠模式、代理模式)

三、代碼示例

img

1.定義共同的方法和行為

package com.ultiwill.strategy;

public interface PayStrategy {

    /**
     * 共同的行為方法
     * @return
     */
    String toPayHtml();

}

2. 三種具體策略的實現 (阿里支付, 微信支付, 小米支付)

package com.ultiwill.strategy.impl;

import com.ultiwill.strategy.PayStrategy;

/**
 * @author chong.zuo
 * @date 2020/9/24 15:21
 */
public class AliPayStrategy implements PayStrategy {

    @Override
    public String toPayHtml() {
        return "調用阿里支付...AliPayStrategy";
    }
}
package com.ultiwill.strategy.impl;

import com.ultiwill.strategy.PayStrategy;

/**
 * @author chong.zuo
 * @date 2020/9/24 15:29
 */
public class WeChatPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml() {
        return "調用微信支付...WeChatPayStrategy";
    }
}
package com.ultiwill.strategy.impl;

import com.ultiwill.strategy.PayStrategy;

/**
 * @author chong.zuo
 * @date 2020/9/24 15:34
 */
public class XiaomiPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml() {
        return "調用小米支付...XiaomiPayStrategy";
    }
}

3. 枚舉類定義映射地址

package com.ultiwill.strategy.enums;

import org.apache.commons.lang.StringUtils;

/**
 * 枚舉
 * @author chong.zuo
 * @date 2020/9/24 15:45
 */
public enum PayEnumStrategy {
    /**
     * 阿里支付
     */
    ALI_PAY("1","com.ultiwill.strategy.impl.AliPayStrategy"),
    /**
     * 微信支付
     */
    WECHAT_PAY("2","com.ultiwill.strategy.impl.WeChatPayStrategy"),
    /**
     * 小米支付
     */
    XIAOMI_PAY("3","com.ultiwill.strategy.impl.XiaomiPayStrategy");


    private String code;
    private String className;

    PayEnumStrategy() {
    }

    PayEnumStrategy(String code, String className) {
        this.code = code;
        this.className = className;
    }

    public static String getClassNameByCode(String code) {
        String className = "";
        if (StringUtils.isEmpty(code)) {
            return className;
        }

        for (PayEnumStrategy e : PayEnumStrategy.values()) {
            if (e.code.equalsIgnoreCase(code)) {
                className = e.className;
                break;
            }
        }
        return className;
    }

    public String getCode() {
        return code;
    }

    public String getClassName() {
        return className;
    }
    
}

4.工廠類反射執行

package com.ultiwill.strategy.factory;

import com.ultiwill.strategy.PayStrategy;
import com.ultiwill.strategy.enums.PayEnumStrategy;

/**
 * @author chong.zuo
 * @date 2020/9/24 16:10
 */
public class StrategyFactory {

    /**
     * 使用策略工廠獲取具體策略實現
     * @param code
     * @return
     */
    public static PayStrategy getPayStrategy(String code) {
        try {
            return (PayStrategy) Class.forName(PayEnumStrategy.getClassNameByCode(code)).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

5.上下文獲取具體策略

package com.ultiwill.strategy.context;

import com.ultiwill.strategy.PayStrategy;
import com.ultiwill.strategy.enums.PayEnumStrategy;
import com.ultiwill.strategy.factory.StrategyFactory;
import org.apache.commons.lang.StringUtils;

/**
 * 上下文
 *
 * @author chong.zuo
 * @date 2020/9/24 15:41
 */
public class PayContextStrategy {

    /**
     * 獲取具體的策略實現
     *
     * @param code
     * @return
     */
    public static String toPayHtml(String code) {
        if (StringUtils.isBlank(code)) {
            return "code不能為空...";
        }
        PayStrategy payStrategy = StrategyFactory.getPayStrategy(code);
        if (payStrategy == null) {
            return "沒有找到具體的策略...";
        }
        return payStrategy.toPayHtml();
    }
}

四、測試

controller:

package com.ultiwill.controller;

import com.ultiwill.strategy.context.PayContextStrategy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author c
 * @date 2020/5/14 9:59
 */
@RestController
public class TestController {

    @RequestMapping("/helloworld")
    public String hello(String code) {
        return PayContextStrategy.toPayHtml(code);
        /*if ("0".equals(code)) {
            return "調用阿里支付...AliPayStrategy";
        } else if ("1".equals(code)) {
            return "調用微信支付...AliPayStrategy";
        } else if ("2".equals(code)) {
            return "調用小米支付...AliPayStrategy";
        }
        return "調用接口不存在";
        */
    }

}

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ultiwill</groupId>
    <artifactId>springboot-jar</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.8.RELEASE</version>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                    <mainClass>com.ultiwill.Application</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>

img

五、結果

img

img

六、在spring中通過Autowired注解實現策略模式

使用AutowireCapableBeanFactory手動注入

使用.newInstance();創建對象的話,如果其他對象都使用Spring Autowired,還需要手動創建所有依賴的Bean:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void process() {
  MyBean obj = new MyBean();
  beanFactory.autowireBean(obj);
  // obj will now have its dependencies autowired.
}

本例中可以使用

private @Autowired AutowireCapableBeanFactory beanFactory;
    /**
     * 使用策略工廠獲取具體策略實現
     * @param code
     * @return
     */
    public  PayStrategy getPayStrategy(String code) {
        String className = PayEnumStrategy.getClassNameByCode(code);
        try {
            PayStrategy str = (PayStrategy) Class.forName(className).getDeclaredConstructor().newInstance();
            beanFactory.autowireBean(str);
            return str;
        } catch (InstantiationException |
                NoSuchMethodException |
                ClassNotFoundException |
                IllegalAccessException |
                InvocationTargetException e) {
            e.printStackTrace();
        }
       return null;
    }

使用Map<String,?> 自動注入

先附上如下的代碼:

public interface TalkService {
    void talk(String content);
}
@Service(value = "withSisterTalkService")
public class WithSisterTalkService implements TalkService {
    @Override
    public void talk(String content) {
        System.out.println(this.getClass().getName() + ":" + content);
    }
}
@Service(value = "withGirlFriendTalkService")
public class WithGirlFriendTalkService implements TalkService {
    @Override
    public void talk(String content) {
        System.out.println(this.getClass().getName() + ":" + content);
    }
}
@Service
public class TalkServiceStrategyContext implements TalkService {

    private Map<String, TalkService> strategyMap = new ConcurrentHashMap<>();
     
    @Autowired
    public TalkServiceStrategyContext(Map<String, TalkService> strategyMap) {
        this.strategyMap.clear();
        this.strategyMap.putAll(strategyMap);
    }
     
    @Override
    public void talk(String content) {
     
    }

}

注意,這里必須是Map<String, TalkService>類型!

@Autowired
private Map<String, TalkService> talkServiceMap;

    @GetMapping(value = "doTest")
    public String doTest() {
        Set<String> strings = talkServiceMap.keySet();
        for (String string : strings) {
            System.out.println(string + ":" + talkServiceMap.get(string).toString());
        }
        return this.getClass().getName();
    }


其訪問測試controller后,打印的信息如下:

talkServiceStrategyContext:com.haiyang.onlinejava.complier.service.impl.TalkServiceStrategyContext@2f0b1419
withGirlFriendTalkService:com.haiyang.onlinejava.complier.service.impl.WithGirlFriendTalkService@1cf19a02
withSisterTalkService:com.haiyang.onlinejava.complier.service.impl.WithSisterTalkService@1ef3c76d

看了后感覺很奇怪,在上方只定義了一個map<String,TalkService>的map,居然它就能自動找到實現了TalkService的所有bean,並將service的beanName作為了key,感覺還是牛逼啊,spring的注解居然還能這樣用。

然后簡單看了下Autowired的源碼,其javaDoc文檔里也有說明:

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

 * Marks a constructor, field, setter method or config method as to be

 * autowired by Spring's dependency injection facilities.
   *

 * <p>Only one constructor (at max) of any given bean class may carry this

 * annotation, indicating the constructor to autowire when used as a Spring

 * bean. Such a constructor does not have to be public.
   *

 * <p>Fields are injected right after construction of a bean, before any

 * config methods are invoked. Such a config field does not have to be public.
   *

 * <p>Config methods may have an arbitrary name and any number of arguments;

 * each of those arguments will be autowired with a matching bean in the

 * Spring container. Bean property setter methods are effectively just

 * a special case of such a general config method. Such config methods

 * do not have to be public.
   *

 * <p>In the case of multiple argument methods, the 'required' parameter is

 * applicable for all arguments.
   *

 * <p>In case of a {@link java.util.Collection} or {@link java.util.Map}

 * dependency type, the container will autowire all beans matching the

 * declared value type. In case of a Map, the keys must be declared as

 * type String and will be resolved to the corresponding bean names.
   *

 * <p>Note that actual injection is performed through a

 * {@link org.springframework.beans.factory.config.BeanPostProcessor

 * BeanPostProcessor} which in turn means that you <em>cannot</em>

 * use {@code @Autowired} to inject references into

 * {@link org.springframework.beans.factory.config.BeanPostProcessor

 * BeanPostProcessor} or

 * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}

 * types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}

 * class (which, by default, checks for the presence of this annotation).
   *

 * @author Juergen Hoeller

 * @author Mark Fisher

 * @since 2.5

 * @see AutowiredAnnotationBeanPostProcessor

 * @see Qualifier

 * @see Value
   */
   @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
   @Retention(RetentionPolicy.RUNTIME)
   @Documented
   public @interface Autowired {

   /**

    * Declares whether the annotated dependency is required.

    * <p>Defaults to {@code true}.
      */
      boolean required() default true;

}

關注這句:

In case of a java.util.Collection or java.util.Map dependency type, the container will autowire all beans matching the declared value type. In case of a Map, the keys must be declared as type String and will be resolved to the corresponding bean names.

它大致是說Autowired當使用在Collection里時,會將所申明類的所有實現類都放在那個指定的Collection里;

如果Autowired和map使用的話呢,它將它bean的名稱作為key,所有的bean作為value.

使用Set<?>自動注入

如果不想使用bean的名字作為map的Key的話,我們可以自定義尋址方式,自動注入時候使用Set<?>:

public interface Strategy {
    void doStuff();
    StrategyName getStrategyName();
}
public enum StrategyName {
  StrategyA,
  StrategyB,
  StrategyC
}
@Component
public class StrategyA  implements Strategy{
  @Override
  public void doStuff() {
      //implement algorithm A here 
  }
  @Override 
  public StrategyName getStrategyName() {
    return StrategyName.StrategyA;
  }
}
@Component
public class StrategyB  implements Strategy{
  @Override
  public void doStuff() {
     //implement algorithm B here
   }
  @Override
  public StrategyName getStrategyName() {
     return StrategyName.StrategyB;
  }
}
@Component
public class StrategyC  implements Strategy{
  @Override
  public void doStuff() {
  }
  @Override
  public StrategyName getStrategyName() {
    return StrategyName.StrategyC;
  }
}
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.stereotype.Component;
@Component
public class StrategyFactory {
  private Map<StrategyName, Strategy> strategies;
  
  @Autowired
  public StrategyFactory(Set<Strategy> strategySet) {
     createStrategy(strategySet);
  }
  
  public Strategy findStrategy(StrategyName strategyName) {
     return strategies.get(strategyName);
  }
  private void createStrategy(Set<Strategy> strategySet) {
      strategies = new HashMap<StrategyName, Strategy>();
      strategySet.forEach( 
   strategy ->strategies.put(strategy.getStrategyName(), strategy));
}
}

Now we can inject StrategyFactory using @Autowired annotation. Here is the sample code using our StrategyFactory.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SomeService {
  @Autowired
  private StrategyFactory strategyFactory;
public void findSome(){  
 
 // Now get the strategy by passing the name 
 Strategy strategy = strategyFactory.findStrategy(StrategyName.StrategyA);
   // you can now call the methods defined in strategy.
    strategy.doStuff();
  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM