前语
本文将论述规划形式中的战略形式
,包含战略形式的运用场景、结构源码剖析等,最后归纳论述下战略形式的优缺陷。
期望能够帮忙大家更好的了解战略形式。@空歌白石
战略形式界说
战略形式(Strategy Pattern)
又被称之为政策形式(Policy Pattern)
,它是将界说的算法家族、分别封装起来,让它们之间能够彼此替换,从而让算法的改变不会影响到运用算法的用户。能够最大程度的防止if else
以及switch
语句。
战略形式
属于行为型形式。
运用场景
线上线下付出办法挑选
在线上线下付出时,能够挑选付出宝、微信付出、云闪付,每种办法的挑选都能够完结付款
的行为,而且只能挑选一种付出办法完结。
出行办法
假定我需求从上海到石家庄,有哪些出行办法能够挑选呢?我们能够挑选飞机、高铁、动车、普速列车、驾车、大巴等等多种办法。可是不管通过哪种办法,都能够从上海抵达石家庄,而且在同一时间只要一种交通办法可挑选。
个人所得税核算
个人所得税的核算分了不同的阶梯,每个阶梯核算的税率不同,每个收入金额中只要一个所得税税率。
总结
战略形式首要包含以下几种场景:
- 假定系统中有很多类,而他们的差异只是在于他们的
行为
不同。 - 一个系统需求动态的在几种算法中挑选其间的一种。
- 需求屏蔽算法规矩。防止
if else
战略形式的完成
我们首先看下战略形式的类图。
界说一致行为笼统
Strategy
的接口负责界说有哪些行为需求履行。
public interface DragonSlayingStrategy {
void execute();
}
界说详细行为履行者
以下我们界说了三种不同的履行战略,分别为Melee
,Projecttile
,Spell
。
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
分为两种完成,分别为SimpleInstantiationStrategy
,CglibSubclassingInstantiationStrategy
。但有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
处理字符串时经常存在拆分的场景,Guava
的Splitter
便是其间一个很好的工具类。在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);
}
fixedLength
和on
办法中详细完成了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();
}
};
}
});
}
优缺陷
优点
- 战略形式符合开闭准则,便于维护扩展。
- 防止运用多重条件搬运语句
- 如
if else
,swtich
等,防止了臃肿的条件语句
- 如
- 运用战略形式能够提高算法的保密性和安全性
- 能够把办法进行愈加合理的封装。
缺陷
- 客户端有必要知道一切的战略,而且自行决定运用哪一种战略。
- 代码中会发生非常多的战略类,增加战略的维护本钱。
结束语
规划形式能够使得代码愈加高雅,增强代码的扩展性。可是万万不能为了规划形式而规划形式,如果真的这样做了,反而会拔苗助长,画蛇添足,大幅度增加代码复杂度和降低可维护性。只要在充沛的剖析业务场景、代码结构的前提下合理的运用规划形式,才干发挥出规划形式最大的效果。
最后一句:规划形式是道法
,并不是术法
。了解内涵最为重要。