一步步学习java并发编程模式之Active Object模式(四)改进后的java实现

来源:互联网 发布:淘宝私人定制怎么进 编辑:程序博客网 时间:2024/06/15 16:46

         上一篇博客一步步学习java并发编程模式之Active Object模式(三)中我们已经按照Active Object模式的各个角色,用java的包进行了分层,也明白了各个角色的职责和调用关系。模拟的代码存在什么问题呢?对于大侠来说,可能看一眼代码层次、组织和调用关系就能指出很多问题。评价一个模块的好坏,往往可以通过新增一个类似功能,看看我们需要改动的代码量和改动的难易程度来判断。对于好的软件模块,是符合开闭原则的。现在我们以新增1个类似服务的方式,来慢慢发现存在的问题。

      

        假如我们需要提供一个耗时的计算2个经纬度之间路网距离的服务,真正的服务提供类在Active Object中是Servant角色。我们可以模仿WeatherServant来编写DistanceServant。存在这样1个问题,如果计算天气或路网距离的实现细节改变了,那我们必须要修改我们模拟active object框架的源码,这就是模拟代码存在的第一个问题。现在是高层抽象(active object框架)依赖于低层细节(DistanceServant和WeatherServant),这是典型的设计坏味道


        现在实际的服务类我们已经有了,怎么将网距离服务用Active Object模式进行封装呢?这个其实比较简单,直接按照天气计算服务的封装服务,改下各个角色的名称和实现代码即可。这种方式有点简单粗暴,很不负责任,坏处很多,很重要的一点就是产生重复代码。但是如果我们发现所有代码都不能复用的时候,cpoy和paste是我们唯一的选择了。如果出现这种我们需要大面积cpoy和paste的时候,意味着我们的代码存在缺陷,需要进行重构解决。

        现在我们按照TDD(测试驱动开发)的思想,先写我们的测试用例。我们可以参考天气的测试类ActiveObjectTest,很容易写出如下的测试类:

public static void testAsyncCall() throws Exception    {        // 1.调用路网距离计算服务,开始计算2个位置之间的距离        DistanceFuture future = DistanceProxy.getDistance(100,50);        // 2.当前线程没有阻塞,仍然可以继续执行.        System.out.println("I am still running.");        // 3.The current thread is not blocked, do something else here...        // Thread.sleep(5 * 1000);        // 4.与天气计算结果无关的代码执行完毕.        System.out.println("Now,i really need distance result to continue.");        // 5.如果路网距离计算还没有结束,那么当前线程挂起,等候计算完成.        int distance = future.get();        System.out.println("路网距离是:" + distance);    }
        很容易看出,天气和路网距离的测试代码是很相似的。这并不是重复代码,这意味着类似的服务,调用者的调用方式相同,这样的一致性是很好的,可以减少调用者的学习成本。现在eclipse会报错,因为目前我们还没有编写DistanceFuture和DistanceProxy的实现。回顾天气计算服务的代码,我们发现到目前为止DistanceFuture和DistanceProxy不能复用的。于是我们不得不写一个DistanceFuture和DistanceProxy,按照eclipse的报错提示,我们一步步copy and paste天气服务的相关代码,最终为了完成距离服务,我们需要要部拷贝和修改天气服务中的所有类,因为这些类都是实现类不能复用。至此我们发现了第二个问题:代码不能复用,不能复用就意味着重复代码,这也是典型的代码坏味道

        主要是因为我们第三篇博客,违反了面向对象的solid原则中的DIP和OCP原则。现在我们使用java泛型和面向接口编程的原则,重构下之前的代码。由于不同的任务返回结果不同,但是却有着同样的操作接口Future,所以Future必须支持泛型,来消除返回值类型差异对代码的影响。

package frame.future;public class Future<T>{    private boolean isDone = false;    private T result = null;    public T get()    {        while (!isDone)        {        }        return result;    }    public void setDone(boolean isDone)    {        this.isDone = isDone;    }    public void setResult(T result)    {        this.result = result;    }}
下面我们编写servant接口和对应的天气实现类
package frame.servant;public interface Servant<T>{    public  T call() throws Exception;}
package frame.servant;import activeobject.aty.result.WeatherResult;public class WeatherServant implements Servant<WeatherResult> {@Overridepublic WeatherResult call() throws Exception {Thread.sleep(2 * 1000);WeatherResult result = new WeatherResult();result.setTemperature(28);result.setWindDirection("西北风");result.setDampness("74%");return result;}}

接着我们编写通用的method request
package frame.methodrequest;import frame.future.Future;import frame.servant.Servant;public abstract class MethodRequest<T> {protected Servant<T> servant;protected Future<T> future;public MethodRequest(Servant<T> servant, Future<T> future) {}public void call() {try {T result = servant.call();future.setResult(result);future.setDone(true);} catch (Exception e) {}}}

由于activation list只依赖于method request,已经有了method request,现在我们编写activation list
package frame.activationlist;import java.util.ArrayList;import java.util.List;import frame.methodrequest.MethodRequest;public class ActivationList{    private List<MethodRequest> requestList = new ArrayList<MethodRequest>();    public synchronized void insertTask(MethodRequest request)    {        requestList.add(request);    }    public synchronized void removeTask(MethodRequest request)    {        requestList.remove(request);    }    public synchronized boolean isEmpty()    {        return requestList.size() == 0;    }    public synchronized MethodRequest popFirst()    {    MethodRequest e = requestList.get(0);        requestList.remove(0);        return e;    }}

现在我们遇到了一点小问题,ActivationList在eclipse下会报黄色警告,因为MethodRequest是泛型的,里面含有返回值的类型信息。但是对于ActivationList来说根本不会在意返回值类型,只要是method request都可以放入ActivationList中。如果将ActivationList修改成泛型类,显然也是不合理的。解决方法是:将MethodRequest替换成MethodRequest<?>。
package frame.activationlist;import java.util.ArrayList;import java.util.List;import frame.methodrequest.MethodRequest;public class ActivationList{    private List<MethodRequest<?>> requestList = new ArrayList<MethodRequest<?>>();    public synchronized void insertTask(MethodRequest<?> request)    {        requestList.add(request);    }    public synchronized void removeTask(MethodRequest<?> request)    {        requestList.remove(request);    }    public synchronized boolean isEmpty()    {        return requestList.size() == 0;    }    public synchronized MethodRequest<?> popFirst()    {    MethodRequest<?> e = requestList.get(0);        requestList.remove(0);        return e;    }}


下面我们编写scheduler角色

package frame.scheduler;import frame.activationlist.ActivationList;import frame.methodrequest.MethodRequest;public class TaskScheduler implements Runnable{    private ActivationList safeRequestList = new ActivationList();    public TaskScheduler()    {        }    public void insertRequest(MethodRequest methodRequest)    {        safeRequestList.insertTask(methodRequest);    }    @Override    public void run()     { while (true)        {            if (!safeRequestList.isEmpty())            {            MethodRequest request = safeRequestList.popFirst();                request.call();            }        }     } }


接下来是代理类,这个类应该符合客户端的调用习惯
package frame.proxy;import frame.future.Future;import frame.methodrequest.MethodRequest;import frame.scheduler.TaskScheduler;import frame.servant.Servant;public class ExecuteTask{    private static TaskScheduler scheduler = new TaskScheduler();        public static <T> Future<T> execute(Servant<T> servant)    {    Future<T> future = new Future<T>();        MethodRequest<T> request = new MethodRequest<T>(servant,future);        scheduler.insertRequest(request);        return future;    }}

最后我们进行测试
package frame;import frame.future.Future;import frame.proxy.ExecuteTask;import frame.result.WeatherResult;import frame.servant.WeatherServant;public class Test {public static void main(String[] args) {Future<WeatherResult> future = ExecuteTask.execute(new WeatherServant());System.out.println("I am running.");System.out.println(future.get());}}

经过DIP后,改代码已经能够实现复用。如果新增1个类似耗时的距离服务,那么仅仅需要编写对应的servant即可,而该类完全都是业务逻辑,不涉及active object框架性代码。


       


0 0