设计模式之策略模式

    今天一起讨论下设计模式中的策略模式,大家平时在用java的时候会用到类似:Collections.sort(List<T> list)的函数来实现排序功能,其实这个排序功能用到的就是策略模式。可以深究下Colloections.sor的源码去跟踪一下。此处暂时不详细展开,后面再展开。

  策略模式(Strategy Pattern)体现出两个面向对象的原则;封装变化的概念,编程中使用接口,而不是对接口的实现。

 策略模式的定义:定义一组算法,将每个算法都封装起来,并且他们之间可以互换,策略模式使这些算法在客户端调用他们的时候可以互不影响地变化。

 策略模式的意义:使开发人员可以开发出许多可以替换部分组成的软件,并且各个部分是弱连接关系。弱连接的特性使软件具有很强的扩展性,易于维护,更重要的是他可以大大提高软件的可重用性。

 策略模式的组成:

1)抽象策略角色:策略类,通常由一个接口或者是抽象类组成;

2)具体策略角色:实现抽象策略角色的接口,包装了相关的算法和行为。这个一般有多个

3)环境角色:持有一个策略类的引用,最终给客户端调用的。

策略模式的实现:

1)策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。

2)策略模式使得算法可以在不影响到客户端的情况下发生变化。使用策略模式可以把行为和环境分割开来。

3)环境类负责维持和查询行为类,各种算法则在具体策略中提供。由于算法和环境独立开来,算法的修改都不会影响环境和客户端。

策略模式的编写步骤

1)对策略对象定义一个公共接口。

2)编写策略类,该类实现了上面的公共接口。

3)在使用策略对象的类中保存一个对策略对象的引用。环境类。

4)在使用策略对象的类中,实现对策略对象的set和get方法(注入)或者使用构造方法完成赋值。---这里说的是上面的环境类。

注意,这里可以去参看JDK Collections类的源代码来进行很好的学习。

例子:

这里举一个策略类的例子,按照策略模式编写的步骤来操作:

第一步:

Strategy.java


package com.java.strategy;

/**
 * 
 * 
 * @author bluesky
 * 第一步:对策略对象定义一个公共接口。
 *
 */
public interface Strategy {
	
	public int caculation(int a,int b);

}

第二步:

编写策略类,该类实现了上面的公共接口。

共有加减乘除四个具体的策略类:

1.AddStrategy.java--->加法类

package com.java.strategy;


/**
 * 
 * @author bluesky
 * 编写策略类,该类实现了上面的公共接口。这里具体功能是现实了加法运算
 */
public class AddStrategy implements Strategy {

	@Override
	public int caculation(int a, int b) {
		// TODO Auto-generated method stub
		return a+b;
	}

}
2.SubtraceStrategy.java--->减法类

package com.java.strategy;

/**
 * 
 * @author bluesky
 * 编写策略类,该类实现了上面的公共接口。这里具体功能是实现了减法运算
 */
public class SubtraceStrategy implements Strategy {

	@Override
	public int caculation(int a, int b) {
		// TODO Auto-generated method stub
		return a-b;
	}

}

3.MultiplyStrategy.java --->乘法类

package com.java.strategy;


/**
 * 
 * @author bluesky
 * 编写策略类,该类实现了上面的公共接口。这里具体功能是实现了乘法运算
 */
public class MultiplyStrategy implements Strategy {

	@Override
	public int caculation(int a, int b) {

		return a*b;
		
	}

}

4.DivStrategy.java --->除法类

package com.java.strategy;

/**
 * 
 * @author bluesky
 * 编写策略类,该类实现了上面的公共接口。这里具体功能是实现了除法运算
 */
public class DivStrategy implements Strategy {

	@Override
	public int caculation(int a, int b) {

		return a/b;
	}

}

第三步:实现环境类

Environment.java

环境类里面也有和接口中的同一个方法,然后调用接口类中的方法即可。

package com.java.strategy;

/**
 * 
 * 
 * @author bluesky
 *
 *在使用策略对象的类中保存一个对策略对象的引用。
 */
public class Environment {

	private Strategy strategy;
	
	public Environment(Strategy strategy) {
		
		this.strategy = strategy;
	}

	public Strategy getStrategy() {
		return strategy;
	}

	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}
	
	//关键的一个函数
	public int caculation(int a,int b){
		return strategy.caculation(a, b);
	}
	
}

第四步:客户端调用

Cletent.java

package com.java.strategy;

public class Client {

	/**
	 * @param args
	 * 客户端调用
	 */
	public static void main(String[] args) {
		int a = 40;
		int b = 8;
		
		AddStrategy addStrategy = new AddStrategy();
		//环境端初始化
		Environment enviroment = new Environment(addStrategy);
		System.out.println("add: "+enviroment.caculation(a, b));
		
		SubtraceStrategy subStrategy = new SubtraceStrategy();
		//直接通过环境段的setter来改变算法接口的行为,而且不影响其它的
		enviroment.setStrategy(subStrategy);
		System.out.println("substract: "+enviroment.caculation(a, b));
		
		MultiplyStrategy multiplyStrategy = new MultiplyStrategy();
		//直接通过环境段的setter来改变算法接口的行为,而且不影响其它的
		enviroment.setStrategy(multiplyStrategy);
		System.out.println("multiply: "+enviroment.caculation(a, b));
		
		DivStrategy divStrategy = new DivStrategy();
		//直接通过环境段的setter来改变算法接口的行为,而且不影响其它的
		enviroment.setStrategy(divStrategy);
		System.out.println("Div: "+enviroment.caculation(a, b));
	}

}

结果:

add: 48
substract: 32
multiply: 320
Div: 5

策略模式相对来说实现的步骤比较固定,用户需要知道在用的时候需要的实体类。从以上也能看到策略模式的缺点:

1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
2)造成很多的策略类。

这个有个解决办法就是:可以采用工厂方法来进行。工厂方法也是一种设计模式,相对用的也比较多。


举个例子:

一个person类,要求用策略模式进行排序,按照姓名、年龄、ID的方式升序,如果姓名、年龄相等,则按照ID排序。

首先定义person类:

package com.java.strategydemo;

public class Person {
	
	public String name;
	public int age;
	public int id;
	
	public Person(String name,int age,int id) {
        this.name = name;
        this.id = id;
        this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	@Override
	public String toString() {
		String ret = "name: "+ name+" age: "+age+" id: "+id+"\n";
		return ret;
	}
 
}

然后定义策略类(这里的策略类其实是实现了java标准API里面的Comparator的接口,这个接口就是前面讲到的接口),此处定义升序和降序两个类:

升序:

AscendSort.java

package com.java.strategydemo;

import java.util.Comparator;

public class AscendSort implements Comparator<Person> {

	@Override
	public int compare(Person o1, Person o2) {
		//mingzi nianling id
		int nameC = o1.name.compareTo(o2.name);
		int ageC = o1.age - o2.age;
		int idC = o1.id - o2.id;
		
		if(nameC!=0){
			return nameC;
		}else{
			if(ageC!=0){
				return ageC;
			}else{
				return idC;
			}
		}
		
	}

}

降序:

DesendSort.java

package com.java.strategydemo;

import java.util.Comparator;

public class DescendSort implements Comparator<Person> {

	@Override
	public int compare(Person o1, Person o2) {
		//mingzi nianling id
		int nameC = o2.name.compareTo(o1.name);
		int ageC = o2.age - o1.age;
		int idC = o2.id - o1.id;
		
		if(nameC!=0){
			return nameC;
		}else{
			if(ageC!=0){
				return ageC;
			}else{
				return idC;
			}
		}
		
	}

}

最后是测试类:

TestMain.java

package com.java.strategydemo;

import java.awt.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class TestMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Person p1 = new Person("zhangsan", 20, 1);
		Person p2 = new Person("zhangsan", 21, 3);
		Person p3 = new Person("zhangsan", 21, 2);
		Person p4 = new Person("lisi", 30, 13);
		Person p5 = new Person("wangwu", 10, 12);
		Person p6 = new Person("mazi", 50, 10);
		
		java.util.List<Person> list = new ArrayList<Person>();
		list.add(p1);
		list.add(p2);
		list.add(p3);
		list.add(p4);
		list.add(p5);
		list.add(p6);
		
		Collections.sort(list, new AscendSort());
		
		Iterator<Person> it = list.iterator();
		while(it.hasNext()){
			Person p = it.next();
			System.out.println(p);
		}
		System.out.println("===================================");
		Collections.sort(list, new DescendSort());
		Iterator<Person> it2 = list.iterator();
		while(it2.hasNext()){
			Person p = it2.next();
			System.out.println(p);
		}

	}

}

输出结果如下:

name: lisi age: 30 id: 13


name: mazi age: 50 id: 10


name: wangwu age: 10 id: 12


name: zhangsan age: 20 id: 1


name: zhangsan age: 21 id: 2


name: zhangsan age: 21 id: 3


===================================
name: zhangsan age: 21 id: 3


name: zhangsan age: 21 id: 2


name: zhangsan age: 20 id: 1


name: wangwu age: 10 id: 12


name: mazi age: 50 id: 10


name: lisi age: 30 id: 13


就是按照我们i所期望的排序策略进行排序了。这里的Collections其实就是我们前面所讲的环境类,在环境类里面可以调用升序和降序的实际策略类,接口这块毫无感知具体的变化。当然缺点也暴露了,你必须知道这里面实现了Comaparator接口的具体的排序类。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值