- 我们在做一些项目的时候经常会遇到有很多种类的算法的出现,它是针对我们同一个对象需要的不同的表现形式做出的处理。
- 比如说在设计模式这本经典的书上写了一个问题,设计一个类似于wps的文档软件,我们遇到的一个文档结构的问题:我们在设计文档结构的时候,讲各种类型的文档结构,表格,文字,行列,等。通过组合模式来设计成了一个树状模型,如图
- 设计好过后我们又有了一个问题出现了,格式化的问题,即如何对我们生成的文档进行我们指定的格式化。
- 已知文档的格式化会涉及到不同的算法,我们的第一想法是在组合模式种最大的接口上设置一个属性,就是格式化的对象,它能够通过if-else来对我们需要的格式化类型进行转换,但是这样的话我们的代码的灵活性大幅度的降低了,我们每一次需要设计一个新算法的时候就会修改源代码并且修改每一个Graph(就是所有的行列的父类)对象的子类添加else操作,这样做肯定会怎加我们的负担。
- 第一个想法,我第一个想到的是用访问者模式,这也是一种设计模式,不懂的可以去看一看我的文章简单了解一下,Graph对象作为被访问者,我们的各种的算法作为访问者来访问,这种想法可以一定程度上的解决了硬编码的问题,但是随之而来又出现了问题 1.我们的访问者模式性能被浪费了,在访问者模式种我们的访问者会定义许多的抽象方法来针对不同的图元,但是在这个例子中,我们的不同的图元是可以执行相同的操作的,这个性能被浪费了。2.在访问者模式中,我们的被访问者是不能够更改自己所需要的访问者的方法的,但是在这个例子中我们明显需要每个对象有不同的算法,来展示不同的格式化样式。 所以不适用访问者模式来实现
- 策略模式:在设计模式这本书上作者给我们使用了一个策略模式来实现了所有图元的格式化操作。下图是我们书上的几张图:
- 作者设计了一个compositor对象来作为说有格式化对象的父类,即应对不同格式化的策略对象,它的具体的子类就实现了它内部的compose来实现不同的格式化操作。而我们定义的composition即是来设置不同的策略的对象,这个类还是我们上面的Graph的子类,它也是所有图元的父类,即所有的图元通过它来实现,相当于它是父类和图元之间的一个中介,在它的内部,有一个属性,是它下面的子类对象,还有一个属性是我们的Compose对象,我们可以给它设置不同的格式化对象。 没看懂我们看一下例子:
首先是我们的Graph对象 自处我的组合设置的比较简单,为了给大家简单的展示一下:
//我这里只写了一个 只是为了这个例子 不止这一个
public interface Graph {
public void insert();
}
我们的composition对象 它是为了递归我们的子类执行格式化算法。
public class Composition implements Graph{
protected Compositor compositor;
protected List<Graph> graph=new ArrayList<>();
public void setCompositor(Compositor compositor){//设置我们的对应的策略
this.compositor=compositor;
}
public void setGraph(Graph graph){//设置自己的子图元对象
this.graph.add(graph);
}
public void insert(){
for(int i=0;i<graph.size();i++){
graph.get(i).insert();//递归调用自己所包含的图元对象的格式化,对每个子类格式化
}
compositor.compose();//执行格式化算法
}
}
接下来是我们的实现子类
这个就是我们平时wps看到的一行 的行对象
//行对象
public class Row extends Composition{
public void insert(){
for(int i=0;i<graph.size();i++){
graph.get(i).insert();//递归调用自己所包含的图元对象的格式化,对每个子类格式化
}
System.out.print("row执行");
compositor.compose();//执行格式化算法
}
}
我们可以在行中存放图片 这个图片也是一个实现对象 这两个方法我都让它重写了composition的insert方法 加了一个打印而已
//图片对象
public class Image extends Composition{
public void insert(){
for(int i=0;i<graph.size();i++){
graph.get(i).insert();//递归调用自己所包含的图元对象的格式化,对每个子类格式化
}
System.out.print("image执行");
compositor.compose();//执行格式化算法
}
}
接下来则是我们的策略格式化算法的父类
里面一个compose就是执行格式化操作
//格式化操作的父类对象
public interface Compositor {
public void compose();
}
以及它的实现子类
数组的格式化算法
//具体的实现算法
public class ArrayCompositor implements Compositor{
@Override
public void compose() {
System.out.println("数组的格式化");
}
}
文本的格式化算法
//对文本的格式化算法
public class TextCompositor implements Compositor{
@Override
public void compose() {
System.out.println("执行对文本的格式化算法");
}
}
然后一个main方法来调用这些操作
public class Main {
public static void main(String[] args){
Row row=new Row();//创建一个图元对象:行,一行里面有3个图片
Image image1=new Image();//创建这三个图片
Image image2=new Image();
Image image3=new Image();
//讲图片放入行中 作为其子图元
row.setGraph(image1);
row.setGraph(image2);
row.setGraph(image3);
//创建算法
TextCompositor textCompistor=new TextCompositor();//创建文本算法
ArrayCompositor arayCompositor=new ArrayCompositor();//定义数组算法
//设置这些图元的格式化算法 这里我们给图片定义数组算法, 给行定义文本算法
row.setCompositor(textCompistor);
image1.setCompositor(arayCompositor);
image2.setCompositor(arayCompositor);
image3.setCompositor(arayCompositor);
//对行执行格式化操作
row.insert();
}
}
看一看执行结果:
结语:这个例子非常好的使用了我们的组合模式和策略模式,让我们的格式化类型易于增加,操作简单,去掉了复杂的硬编码,简单的实现了子类的递归调用。
最重要的是分离,将对象可能会出现不同的操作分离开来,形成一个新的接口供人选择。比如你要写一个比较器,那么我们需要将可能会出现不同的比较操作分离开来(因为不同的对象可能会使用不同的比较策略),每个想要使用的对象实现一个比较器做自己的操作。
此处我们使用一个简单的比较器:
//compare接口
public interface Compare<T> {
public int compare(T o1, T o2);
}
=======================================================================================
//dog对象
public class Dog {
private int age;
private String name;
public Dog(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Dog [age=" + age + ", name=" + name + "]";
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
======================================================================================
//重写的dog比较器,通过年龄比较:
public class DogCompare implements Compare<Dog>{
@Override
public int compare(Dog d1, Dog d2) {
// TODO Auto-generated method stub
return d1.getAge()-d2.getAge();
}
}
=======================================================================================
//排序算法 此处我使用的是一个简单的快排 传入参数是我们的比较器和对象数组,我们只需要将比较的方法更改即可
public class Sort {
public void sort(Object[] arr,int left,int right,Compare compare){
if(left>=right){
return;
}
int index1=left,index2=right-1,index=left;
while(index<right){
if(index>index2)//因为我的索引左边不可能大于index(左边的index的值是一直等于index的,所以不用换位置) 右边不可能小于index(因为在和右边换位置时候我的index的值不能变不知道他是比我们原来的值大还是小)
break;
if(compare.compare(arr[index],arr[right])<0){//修改此处的方法换为我们比较器的方法即可
/*swap(arr,index,index1++);*/
index1++;
}else if(compare.compare(arr[index],arr[right])>0){
swap(arr,index--,index2--);
}
index++;
}
swap(arr,++index2,right);
sort(arr,left,--index1,compare);
sort(arr,++index2,right,compare);
}
public static void swap(Object[] arr,int index1,int index2){
Object temp=arr[index1];
arr[index1]=arr[index2];
arr[index2]=temp;
}
}
======================================================================================
//main方法
public class Main {
public static void main(String[] args){
Dog dog[]=new Dog[]{new Dog(4,"张三"),new Dog(2,"李四"),new Dog(1,"王五")};
Sort sort=new Sort();
sort.sort(dog, 0, dog.length-1, new DogCompare());
for(Dog d:dog){
System.out.println(d);
}
}
}
通过这样的操作过后我我们就可以比较各种各样的对象了,只需要实现一个compare接口即可。