热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

可复用的activeo

         上一篇博客一步步学习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
{
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
{
public T call() throws Exception;
}

package frame.servant;
import activeobject.aty.result.WeatherResult;
public class WeatherServant implements Servant {

@Override
public 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
{
protected Servant servant;
protected Future future;
public MethodRequest(Servant servant, Future 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 requestList = new ArrayList();
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> requestList = new ArrayList>();
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();
}
}

}
}
现在编写scheduler类

package frame.scheduler;
import frame.activationlist.ActivationList;
import frame.methodrequest.MethodRequest;
public class TaskScheduler extends Thread
{
private ActivationList safeRequestList = new ActivationList();
public TaskScheduler()
{
this.start();
}
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 Future execute(Servant servant)
{
Future future = new Future();

MethodRequest request = new MethodRequest(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 future = ExecuteTask.execute(new WeatherServant());

System.out.println("I am running.");

System.out.println(future.get());
}
}

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


       


一步步学习java并发编程模式之Active Object模式(四)改进后的java实现,布布扣,bubuko.com


推荐阅读
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
author-avatar
至上励合_安儿_466
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有