一、概述
策略模式:
定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换。策略模式的核心思想在于:使用一种可替代的接口来代替原本写死的代码,这样当我们需要新增一些别的算法的时候,就不再需要修改我们的源代码,而且可以实现代码的可复用性。
二、角色职责与UML图
2.1 角色职责
Strategy(抽象策略)
- 定义算法的公共接口,Context使用这个接口来调用某种策略的算法
ConcreteStrategy(具体策略)
- 具体的算法策略,实现了Strategy的公共接口
- 每一个都是一个不同的算法
Context(上下文)
- 维护一个Strategy的对象
- 通过传入的Strategy对象来调用策略
2.2 UML图
三、示例
实在是没有例子可以举了,就用二叉树的遍历来举例子吧
我们都知道二叉树的遍历有先序、中序、后序和层次遍历这几种方式,这几种方式就可以抽象成我们的策略接口,然后分别实现
当我们需要不同的遍历方式的时候,就可以直接调用了
首先手写一个二叉树
/**
* @author ZhongJing </p>
* @Description 二叉树(链式) </p>
*/
public class MyBinaryTree<T> {
/**
* 本节点的值
*/
private T value;
/**
* 左子树
*/
private MyBinaryTree<T> lChild;
/**
* 右子树
*/
private MyBinaryTree<T> rChild;
/**
* 构造器:用来构造非叶子节点
*
* @param value 节点的值
* @param lChild 左子树
* @param rChild 右子树
*/
public MyBinaryTree(T value, MyBinaryTree<T> lChild, MyBinaryTree<T> rChild) {
this.value = value;
this.lChild = lChild;
this.rChild = rChild;
}
/**
* 构造器:用来构建叶子节点
*/
public MyBinaryTree(T value) {
this.value = value;
lChild = null;
rChild = null;
}
public void setValue(T value) {
this.value = value;
}
public MyBinaryTree<T> getlChild() {
return lChild;
}
public void setlChild(MyBinaryTree<T> lChild) {
this.lChild = lChild;
}
public MyBinaryTree<T> getrChild() {
return rChild;
}
public void setrChild(MyBinaryTree<T> rChild) {
this.rChild = rChild;
}
/**
* 判断本二叉树是否是叶子节点
*
* @return true-是 false-否
*/
public boolean isLeaf() {
return this.lChild == null && this.rChild == null;
}
/**
* 返回本节点的值
*/
public T getValue() {
return (T) this.value;
}
/**
* 遍历二叉树,可选择遍历的策略:先根、后根、中根
*
* @param forEachBinaryTree 遍历二叉树的策略
*/
public void ergodic(ForEachBinaryTree<T> forEachBinaryTree) {
forEachBinaryTree.foreach(this);
}
}
然后再写一个抽象策略,定义二叉树的遍历方式
/**
* @author ZhongJing </p>
* @Description 遍历二叉树抽象类 </p>
*/
public interface ForEachBinaryTree<T> {
void foreach(MyBinaryTree<T> root);
}
先序遍历的策略
/**
* @author ZhongJing </p>
* @Description 先序遍历二叉树 </p>
*/
public class PreorderTraversal<T> implements ForEachBinaryTree<T> {
@Override
public void foreach(MyBinaryTree<T> root) {
// 节点为空直接返回
if (root == null)
return;
System.out.print((root.getValue() == null ? "" : root.getValue() + ", "));
foreach(root.getlChild());
foreach(root.getrChild());
}
}
中序遍历的策略
/**
* @author ZhongJing </p>
* @Description 中序遍历二叉树 </p>
*/
public class InorderTraversal<T> implements ForEachBinaryTree<T> {
@Override
public void foreach(MyBinaryTree<T> root) {
if (root == null)
return;
foreach(root.getlChild());
System.out.print((root.getValue() == null ? "" : root.getValue() + ", "));
foreach(root.getrChild());
}
}
后序遍历的策略
/**
* @author ZhongJing </p>
* @Description 后序遍历二叉树 </p>
*/
public class PostorderTraversal<T> implements ForEachBinaryTree<T> {
@Override
public void foreach(MyBinaryTree<T> root) {
if (root == null)
return;
foreach(root.getlChild());
foreach(root.getrChild());
System.out.print((root.getValue() == null ? "" : root.getValue() + ", "));
}
}
层次遍历的策略
public class LevelTraversal<T> implements ForEachBinaryTree<T> {
// 链表模拟队列
LinkedList<MyBinaryTree<T>> queue = new LinkedList<>();
@Override
public void foreach(MyBinaryTree<T> root) {
// 树根入队
queue.offer(root);
// 队头循环出队,直到队列为空
while (!queue.isEmpty()) {
// 队头元素出队
MyBinaryTree<T> tree = queue.poll();
System.out.print(tree.getValue() + ", ");
// 如果左子树不为空则左子树入队
if (tree.getlChild() != null) {
queue.offer(tree.getlChild());
}
// 如果右子树不为空则右子树入队
if (tree.getrChild() != null) {
queue.offer(tree.getrChild());
}
}
}
}
最后测试
/**
* @author ZhongJing </p>
* @Description 测试二叉树 </p>
*/
public class MyBinaryTreeTest {
public static void main(String[] args) {
/*
创建一个二叉树
1
2 3
4 5 6
*/
MyBinaryTree<Integer> treeNode6 = new MyBinaryTree<>(6);
MyBinaryTree<Integer> treeNode5 = new MyBinaryTree<>(5);
MyBinaryTree<Integer> treeNode4 = new MyBinaryTree<>(4);
MyBinaryTree<Integer> treeNode3 = new MyBinaryTree<>(3, treeNode6, null);
MyBinaryTree<Integer> treeNode2 = new MyBinaryTree<>(2, treeNode4, treeNode5);
MyBinaryTree<Integer> treeNode1 = new MyBinaryTree<>(1, treeNode2, treeNode3);
// 先序
treeNode1.ergodic(new PreorderTraversal<Integer>());
System.out.println();
// 中序
treeNode1.ergodic(new InorderTraversal<Integer>());
System.out.println();
// 后序
treeNode1.ergodic(new PostorderTraversal<Integer>());
System.out.println();
// 层次遍历
treeNode1.ergodic(new LevelTraversal<Integer>());
}
}
运行结果如下:
1, 2, 4, 5, 3, 6,
4, 2, 5, 1, 6, 3,
4, 5, 2, 6, 3, 1,
1, 2, 3, 4, 5, 6,
测试中可以看到,当我们需要不同的策略时候,只需要传入一个新的策略对象就好了