前语

本文将论述规划形式中的战略形式,包含战略形式的运用场景、结构源码剖析等,最后归纳论述下战略形式的优缺陷。

期望能够帮忙大家更好的了解战略形式。@空歌白石

战略形式界说

战略形式(Strategy Pattern)又被称之为政策形式(Policy Pattern),它是将界说的算法家族、分别封装起来,让它们之间能够彼此替换,从而让算法的改变不会影响到运用算法的用户。能够最大程度的防止if else以及switch语句。

战略形式属于行为型形式。

运用场景

线上线下付出办法挑选

在线上线下付出时,能够挑选付出宝、微信付出、云闪付,每种办法的挑选都能够完结付款的行为,而且只能挑选一种付出办法完结。

【规划形式】战略形式(Strategy Pattern)

出行办法

假定我需求从上海到石家庄,有哪些出行办法能够挑选呢?我们能够挑选飞机、高铁、动车、普速列车、驾车、大巴等等多种办法。可是不管通过哪种办法,都能够从上海抵达石家庄,而且在同一时间只要一种交通办法可挑选。

【规划形式】战略形式(Strategy Pattern)

个人所得税核算

个人所得税的核算分了不同的阶梯,每个阶梯核算的税率不同,每个收入金额中只要一个所得税税率。

【规划形式】战略形式(Strategy Pattern)

总结

战略形式首要包含以下几种场景:

  1. 假定系统中有很多类,而他们的差异只是在于他们的行为不同。
  2. 一个系统需求动态的在几种算法中挑选其间的一种。
  3. 需求屏蔽算法规矩。防止if else

战略形式的完成

我们首先看下战略形式的类图。

【规划形式】战略形式(Strategy Pattern)

界说一致行为笼统

Strategy的接口负责界说有哪些行为需求履行。

public interface DragonSlayingStrategy {
  void execute();
}

界说详细行为履行者

以下我们界说了三种不同的履行战略,分别为MeleeProjecttileSpell

public class MeleeStrategy implements DragonSlayingStrategy {
  @Override
  public void execute() {
    LOGGER.info("With your Excalibur you sever the dragon's head!");
  }
}
public class ProjectileStrategy implements DragonSlayingStrategy {
  @Override
  public void execute() {
    LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!");
  }
}
public class SpellStrategy implements DragonSlayingStrategy {
  @Override
  public void execute() {
    LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!");
  }
}

战略调度者

上文中界说了战略笼统以及详细的战略行为,接下来便是如何运用这些战略了。这部分分为两种完成,第二种运用工厂的办法封装的更高雅些。

封装类完成

通过界说一个DragonSlayer类,来封装对详细战略和行的一致履行。

public class DragonSlayer {
  private DragonSlayingStrategy strategy;
  public DragonSlayer(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }
  public void changeStrategy(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }
  public void goToBattle() {
    strategy.execute();
  }
}

详细的履行办法。

DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy());
dragonSlayer.goToBattle();
dragonSlayer.changeStrategy(new ProjectileStrategy());
dragonSlayer.goToBattle();
dragonSlayer.changeStrategy(new SpellStrategy());
dragonSlayer.goToBattle();

能够看出以上的履行中,需求由调用方在详细履行时挑选运用哪种详细的战略。那么能够进行优化吗?能够运用工厂办法进一步的封装。

工厂办法完成

运用简单的工厂办法,完成战略的调度者。

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class DragonSlayerFactory {
    private static Map<String, DragonSlayingStrategy> map = new HashMap<>();
    static {
        map.put(StrategyKey.MELEE, new MeleeStrategy());
        map.put(StrategyKey.PROJECTILE, new ProjectileStrategy());
        map.put(StrategyKey.SPELL, new SpellStrategy());
    }
    private DragonSlayerFactory() {
    }
    public static DragonSlayingStrategy getDragonSlayingStrategy(String strategyKey) {
        if (!map.containsKey(strategyKey)) {
            return null;
        }
        return map.get(strategyKey);
    }
    public static Set<String> getAllStrategy() {
        return map.keySet();
    }
    private interface StrategyKey {
        String MELEE = "melee";
        String PROJECTILE = "Projectile";
        String SPELL = "Spell";
    }
}

结构源码剖析

JDK中的Comparator

Comparator界说了compare办法。

    /**
     * Compares its two arguments for order.  Returns a negative integer,
     * zero, or a positive integer as the first argument is less than, equal
     * to, or greater than the second.<p>
     *
     * The implementor must ensure that {@link Integer#signum
     * signum}{@code (compare(x, y)) == -signum(compare(y, x))} for
     * all {@code x} and {@code y}.  (This implies that {@code
     * compare(x, y)} must throw an exception if and only if {@code
     * compare(y, x)} throws an exception.)<p>
     *
     * The implementor must also ensure that the relation is transitive:
     * {@code ((compare(x, y)>0) && (compare(y, z)>0))} implies
     * {@code compare(x, z)>0}.<p>
     *
     * Finally, the implementor must ensure that {@code compare(x,
     * y)==0} implies that {@code signum(compare(x,
     * z))==signum(compare(y, z))} for all {@code z}.
     *
     * @apiNote
     * It is generally the case, but <i>not</i> strictly required that
     * {@code (compare(x, y)==0) == (x.equals(y))}.  Generally speaking,
     * any comparator that violates this condition should clearly indicate
     * this fact.  The recommended language is "Note: this comparator
     * imposes orderings that are inconsistent with equals."
     *
     * @param o1 the first object to be compared.
     * @param o2 the second object to be compared.
     * @return a negative integer, zero, or a positive integer as the
     *         first argument is less than, equal to, or greater than the
     *         second.
     * @throws NullPointerException if an argument is null and this
     *         comparator does not permit null arguments
     * @throws ClassCastException if the arguments' types prevent them from
     *         being compared by this comparator.
     */
    int compare(T o1, T o2);

Arrays类的compare办法传入了Comparator战略,能够由运用方详细履行详细的比较器。

public static <T> int compare(T[] a, T[] b,
                                Comparator<? super T> cmp) {
    Objects.requireNonNull(cmp);
    if (a == b)
        return 0;
    if (a == null || b == null)
        return a == null ? -1 : 1;
    int length = Math.min(a.length, b.length);
    for (int i = 0; i < length; i++) {
        T oa = a[i];
        T ob = b[i];
        if (oa != ob) {
            // Null-value comparison is deferred to the comparator
            int v = cmp.compare(oa, ob);
            if (v != 0) {
                return v;
            }
        }
    }
    return a.length - b.length;
}

Spring InstantiationStrategy

InstantiationStrategy负责Spring Bean初始化的战略。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.lang.Nullable;
public interface InstantiationStrategy {
	Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
			throws BeansException;
	Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			Constructor<?> ctor, @Nullable Object... args) throws BeansException;
	Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args)
			throws BeansException;
}

InstantiationStrategy分为两种完成,分别为SimpleInstantiationStrategyCglibSubclassingInstantiationStrategy。但有Cglib时,运用CglibSubclassingInstantiationStrategy,不然运用默认的SimpleInstantiationStrategy战略。

public class SimpleInstantiationStrategy implements InstantiationStrategy {
	private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<>();
	/**
	 * Return the factory method currently being invoked or {@code null} if none.
	 * <p>Allows factory method implementations to determine whether the current
	 * caller is the container itself as opposed to user code.
	 */
	@Nullable
	public static Method getCurrentlyInvokedFactoryMethod() {
		return currentlyInvokedFactoryMethod.get();
	}
	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							constructorToUse = clazz.getDeclaredConstructor();
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}
    // 空歌白石:省略剩下代码
public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
	/**
	 * Index in the CGLIB callback array for passthrough behavior,
	 * in which case the subclass won't override the original class.
	 */
	private static final int PASSTHROUGH = 0;
	/**
	 * Index in the CGLIB callback array for a method that should
	 * be overridden to provide <em>method lookup</em>.
	 */
	private static final int LOOKUP_OVERRIDE = 1;
	/**
	 * Index in the CGLIB callback array for a method that should
	 * be overridden using generic <em>method replacer</em> functionality.
	 */
	private static final int METHOD_REPLACER = 2;
	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		return instantiateWithMethodInjection(bd, beanName, owner, null);
	}
	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Constructor<?> ctor, @Nullable Object... args) {
		// Must generate CGLIB subclass...
		return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
	}
    // 空歌白石:省略剩下代码

Guava Splitter Strategy

处理字符串时经常存在拆分的场景,GuavaSplitter便是其间一个很好的工具类。在Splitter中界说了一个Strategy接口。

private interface Strategy {
    Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
}

Splitter的私有构造办法中需求传入详细的Strategy

private final Strategy strategy;
private final int limit;
private Splitter(Strategy strategy) {
    this(strategy, false, CharMatcher.none(), Integer.MAX_VALUE);
}

fixedLengthon办法中详细完成了iterator战略。

public static Splitter fixedLength(final int length) {
    checkArgument(length > 0, "The length may not be less than 1");
    return new Splitter(
        new Strategy() {
            @Override
            public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
                return new SplittingIterator(splitter, toSplit) {
                    @Override
                    public int separatorStart(int start) {
                        int nextChunkStart = start + length;
                        return (nextChunkStart < toSplit.length() ? nextChunkStart : -1);
                    }
                    @Override
                    public int separatorEnd(int separatorPosition) {
                        return separatorPosition;
                    }
                };
            }
        });
}
public static Splitter on(final String separator) {
    checkArgument(separator.length() != 0, "The separator may not be the empty string.");
    if (separator.length() == 1) {
        return Splitter.on(separator.charAt(0));
    }
    return new Splitter(
        new Strategy() {
            @Override
            public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) {
            return new SplittingIterator(splitter, toSplit) {
                @Override
                public int separatorStart(int start) {
                    int separatorLength = separator.length();
                    positions:
                    for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) {
                        for (int i = 0; i < separatorLength; i++) {
                            if (toSplit.charAt(i + p) != separator.charAt(i)) {
                                continue positions;
                            }
                        }
                        return p;
                    }
                    return -1;
                }
                @Override
                public int separatorEnd(int separatorPosition) {
                    return separatorPosition + separator.length();
                }
            };
            }
        });
}
private static Splitter on(final CommonPattern separatorPattern) {
    checkArgument(
        !separatorPattern.matcher("").matches(),
        "The pattern may not match the empty string: %s",
        separatorPattern);
    return new Splitter(
        new Strategy() {
            @Override
            public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
                final CommonMatcher matcher = separatorPattern.matcher(toSplit);
                return new SplittingIterator(splitter, toSplit) {
                    @Override
                    public int separatorStart(int start) {
                        return matcher.find(start) ? matcher.start() : -1;
                    }
                    @Override
                    public int separatorEnd(int separatorPosition) {
                        return matcher.end();
                    }
                };
            }
        });
}

优缺陷

优点

  1. 战略形式符合开闭准则,便于维护扩展。
  2. 防止运用多重条件搬运语句
    1. if elseswtich等,防止了臃肿的条件语句
  3. 运用战略形式能够提高算法的保密性和安全性
    1. 能够把办法进行愈加合理的封装。

缺陷

  1. 客户端有必要知道一切的战略,而且自行决定运用哪一种战略。
  2. 代码中会发生非常多的战略类,增加战略的维护本钱。

结束语

规划形式能够使得代码愈加高雅,增强代码的扩展性。可是万万不能为了规划形式而规划形式,如果真的这样做了,反而会拔苗助长,画蛇添足,大幅度增加代码复杂度和降低可维护性。只要在充沛的剖析业务场景、代码结构的前提下合理的运用规划形式,才干发挥出规划形式最大的效果。

最后一句:规划形式是道法,并不是术法。了解内涵最为重要。

【规划形式】战略形式(Strategy Pattern)