前两篇分别讲了Dynamic Proxy(动态代理模式)和Iterator(迭代器模式),这次笔者来分享Stragety(策略模式)的小小经验。
笔者的设计模式经验是基于Object-Orient Programming(OOP)的,这三篇的blog基本的核心是多态和代码的复用,要服用的目的是避免代码的写死,增加代码的Extensibility(拓展性),而拓展性的基础就是多态。笔者这里的多态基本是属于使用接口实现的,如果说继承中有名句“父类的引用指向子类的对象”,那么在接口实现中就是“接口的引用指向实现类的对象”(此仅为笔者个人的说法)。之所以不经常使用Inheritance(继承)的原因笔者在Dynamic Proxy中就算是说的比较清楚了,就是为了解耦。
现在进入正题,为什么要称这次的设计模式为策略模式呢?依照笔者现在的经验来说就是说由于潜在的可选择方案多,每一种方案可能会导致一致的结果产生也可能导致不同的结果,故此我们需要选择哪种方案去实现就称为策略,譬如:Java中的Comparable & Comparator就是一种策略模式的具体实现例子,而今天笔者就是对这两个接口进行自定义的实现。
1.Question
在我们进行排序的时候,一般我们所最常接触到的排序策略有:冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序和堆排序,笔者学来一句谐音助记:冒择路兮快归堆(大意大概为:冒失地选择道路你离入坟不远了(>﹏<)).
现在,笔者想问一个问题:我们常使用这些排序的地方是不是就是单纯地排整型和浮点型数据?如果笔者问:咱们要对对象排序咋办?
内心独白估计是:。。。。。。尴尬!
对于我们不是计算机专业的更没有经过ACM训练的战五渣来说,算法的应用估计是没什么思路和想法的,请容许笔者说句后悔点的话:当初应该转数信的〒▽〒!跑去ACM打酱油也好啊!
有遗憾才证明自己是活着的(虽然我很不情愿地说这句话)。
回归正题,其实对于对象的排序也可以是通过整型和浮点型数据的排序,因为OO的思维是把它们封装成了属性,排序时上面的排序方式都都是可用的而且还是通过我们熟悉的简单排序的方式实习呢!然而,事情也不会简单到这么快让各位一下子看透了(多数人的思维是盲人摸象般的,很儿戏好吗!)
下面笔者将开始我们多版本的代码迭代来感受下Stragety,有些东西只可意会不可言传(其实是笔者没那个水平一下子将的清楚<-前面这句话其实是骗你的,基于一点:记住一个具体的实例从中提取信息比单纯的说概念要更易于接收,概念的东西是抽象的,抽象的东西实质就是不好好说人话)。
2.Example of data sort(code version1.0)
我们先是来下经典的冒泡排序。
DataSort.java
public class DataSort{
public static void sort(int[] arr){
for(int i = arr.length; i >= 0; i--){//外层控制大循环
for(int j = 0; j < i - 1; j++){//内层控制小循环和交换
if(a[j] > a[j + 1])
swap(a, j, j + 1);
}
}
}
public static void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void print(int[] arr){
for(int i = 0; i < arr.length; i++){
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
Test.java
public class Test{
public static void main(String[] args){
int[] arr = new int[]{1, 11, 8, 22, 66};
DataSort.sort(arr);
DataSort.print(arr);
}
}
如果我们这里需要对浮点型的数据排序,只要重载下以上方法就可以了(重载:同一个类中的方法,其方法同名,参数列表不同,记重载参数列表;重写/覆盖:重写父类中方法的实现,属于两个类间的关系)。
DataSort.java ver1.1
public class DataSort {
public static void sort(int[] arr) {
for (int i = arr.length; i >= 0; i--) {
for (int j = 0; j < i - 1; j++) {
if(arr[j] > arr[j + 1])
swap(arr, j, j + 1);
}
}
}
private static void swap(int[] arr, int j, int i) {
int tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
public static void print(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void sort(double[] arr) {
for (int i = arr.length; i >= 0; i--) {
for (int j = 0; j < i - 1; j++) {
if(arr[j] > arr[j + 1])
swap(arr, j, j + 1);
}
}
}
private static void swap(double[] arr, int j, int i) {
double tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
public static void print(double[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
3.Single object sort(code version1.1)
对单个类的排序我们其实不需要实现接口,以最简单的方法就可以完成。
Goods.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition 商品类
* @created 2018-04-30-23:52
*/
public class Goods implements Comparable{
private double price;
private int weight;
public Goods(double price, int weight) {
this.price = price;
this.weight = weight;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "¥:" + this.getPrice() + "|" + this.getWeight() + "kg";
}
}
ObjDataSort.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition
* @created 2018-04-30-23:56
*/
public class ObjDataSort {
public static void sort(Goods[] obj) {//这里写死了只对Goods一类的支持
for (int i = obj.length; i >= 0; i--) {
for (int j = 0; j < i - 1; j++) {
if(obj[j].getPrice() > obj[j + 1].getPrice())
swap(obj, j, j + 1);
}
}
}
private static void swap(Object[] obj, int j, int i) {
Object tmp = obj[j];
obj[j] = obj[i];
obj[i] = tmp;
}
public static void print(Object[] obj) {
for (int i = 0; i < obj.length; i++) {
System.out.print(obj[i] + " ");
}
System.out.println();
}
}
Test.java
public class Test{
public static void main(String[] args){
Goods[] goods = new Goods[]{new Goods(15.2, 15), new Goods(11.1, 11), new Goods(25.8, 25)};
ObjDataSort.sort(goods);
ObjDataSort.print(goods);
}
}
4.Optimize the codes(code version1.2)
要优化其实就是说要可拓展,让代码复用,要复用一定会有多态。我们先得看得出哪一个地方写死了,再使用多态解决它。
很明显上面的sort中只支持Goods类型,这样可定是不行的,一旦要求改成飞机,汽车了就尴尬了,所以我们肯定要想一个它们中共同的特点来满足能够比较的特点,具体的实现交给它们自己实现。
基于这点思考,笔者做了以下优化。
Comparable.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition 可比较的接口
* @created 2018-05-01-0:03
*/
public interface Comparable {
int compareTo(Object o) throws Exception;
}
Goods.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition 商品类
* @created 2018-04-30-23:52
*/
public class Goods implements Comparable{
private double price;
private int weight;
public Goods(double price, int weight) {
this.price = price;
this.weight = weight;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "¥:" + this.getPrice() + "|" + this.getWeight() + "kg";
}
@Override
public int compareTo(Object o) throws Exception {
if(o instanceof Goods){
Goods g1 = (Goods)o;
return this.getPrice() > g1.getPrice() ? 1 : this.getPrice() < g1.getPrice() ? -1 : 0;
}
else
throw new Exception("unable to compare " + o.getClass() + " to Goods");
}
}
ObjDataSort.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition
* @created 2018-04-30-23:56
*/
public class ObjDataSort {
public static void sort(Comparable[] obj) {
for (int i = obj.length; i >= 0; i--) {
for (int j = 0; j < i - 1; j++) {
try {
if(1 == obj[j].compareTo(obj[j + 1]))
swap(obj, j, j + 1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void swap(Comparable[] obj, int j, int i) {
Comparable tmp = obj[j];
obj[j] = obj[i];
obj[i] = tmp;
}
public static void print(Comparable[] obj) {
for (int i = 0; i < obj.length; i++) {
System.out.print(obj[i] + " ");
}
System.out.println();
}
}
这样各自的类都可进行自己本类对象间的比较,但是我么还是可以看到这还是不完善的,因为这个类里还有其他属性是可以支持比较,也就是还有其他潜在的策略没有支持到,笔者接下来接着改进。
5.Alternative Stragety(code version1.3)
为了避免compareTo的写死,我们是不是应该把比较的具体逻辑交给外面实现,然后再用compareTo调用这个实现,这样由于外部的实现是多种的,我们通过接口规范就实现复用了。
Comparator.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition 比较器接口
* @created 2018-05-01-8:39
*/
public interface Comparator {
int compare(Object o1, Object o2) throws Exception;
}
Goods.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition 商品类
* @created 2018-04-30-23:52
*/
public class Goods implements Comparable{
private double price;
private int weight;
public Goods(double price, int weight) {
this.price = price;
this.weight = weight;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "¥:" + this.getPrice() + "|" + this.getWeight() + "kg";
}
@Override
public int compareTo(final Object o) throws Exception {
if(o instanceof Goods){
Goods g1 = (Goods)o;
Comparator c = new Comparator(){//这是为了偷懒不写一个完整的类
@Override
public int compare(Object o1, Object o2) throws Exception {
if(o1 instanceof Goods && o2 instanceof Goods){
return ((Goods) o1).getWeight() > ((Goods) o2).getWeight() ? 1 : ((Goods) o1).getWeight() < ((Goods) o2).getWeight() ? -1 : 0;
} else
throw new Exception("unable to compare " + o.getClass() + " to Goods");
}
};
return c.compare(this, g1);
}
else
throw new Exception("unable to compare " + o.getClass() + " to Goods");
}
}
ObjDataSort.java
package com.unicorn.strategy;
/**
* @author Unicorn
* @descrition
* @created 2018-04-30-23:56
*/
public class ObjDataSort {
public static void sort(Comparable[] obj) {
for (int i = obj.length; i >= 0; i--) {
for (int j = 0; j < i - 1; j++) {
try {
if(1 == obj[j].compareTo(obj[j + 1]))
swap(obj, j, j + 1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void swap(Comparable[] obj, int j, int i) {
Comparable tmp = obj[j];
obj[j] = obj[i];
obj[i] = tmp;
}
public static void print(Comparable[] obj) {
for (int i = 0; i < obj.length; i++) {
System.out.print(obj[i] + " ");
}
System.out.println();
}
}
这里三步过后就是完全做到比较策略的自主选择了,这就是策略模式了。
简单说说为什么实现了Comparable还要实现Comparator,尽管Comparable也可以做到比较策略的选择,但是每次都需要更改CompareTo的逻辑,这完全比不上我每次更换一个接口实现来的容易,也就是更改原有的实现,不如替换不同的实现;再者,这是一种组件的实现思维,组建之间各自的实现越细分越好,Iterator的Collection接口和Iterator接口也是这样的。
笔者这次就i到此为止了!
感谢各位的支持!
如果note有错漏,恳请各位留下评论指出,笔者会及时改正!
谢谢!