- 浏览: 40593 次
- 性别:
文章分类
最新评论
-
sunheavenvan:
我觉得不存在提炼出有所谓的本质,提炼的目的是获取较为准确的心智 ...
Jdon007四象图坐标表示 -
yangyi:
好像没有提及Seam给测试和OO设计上带来的便宜,不好意思,还 ...
Seam生命周期 -
buptwhisper:
性能一直是个问题
Seam生命周期 -
sulong:
oojdon 写道如果大家站在Gavin King的角度,包括 ...
Seam生命周期 -
may_cauc:
扬长避短吧,任何一种技术的出现都有其初衷。不要轻易否定,在否定 ...
Seam生命周期
http://blog.maxant.co.uk/pebble/2010/11/20/1290288540000.html
Data, Context and Interaction (DCI) is a way to improve the readability of object oriented code. But it has nothing specific to say about things like transactions, security, resources, concurrency, scalability, reliability, or other such concerns.
Services, in terms of stateless EJBs or Spring Services, have a lot to say about such concerns, and indeed allow the cross-cutting concerns like transactions and security to be configured outside of the code using annotations or deployment descriptors (XML configuration), letting programmers concentrate on business code. The code they write contains very little code related to transactions or security. The other concerns are handled by the container in which the services live.
Services however, constrain developers to think in terms of higher order objects which deliver functionality. Object orientation (OO) and DCI let programmers program in terms of objects; DCI more so than OO. In those cases where the programmer wants to have objects with behaviour, rather than passing the object to a service, they can use DCI.
If objects are to provide rich behaviour (ie. behaviour which relies on security, transactions or resources), then they need to somehow be combined with services, which naturally do this and do it with the help of a container, so that the programmer does not need to write low level boiler plate code, for example to start and commit transactions.
The idea here, is to explore combining a service solution with a DCI solution. Comparing SOA to DCI, like I did in my white paper, shows that the DCI context is analagous to the code which calls a service in a SOA solution, and that a context does not really explicitly exist in a SOA solution. However, thinking a little more abstractly, in DCI an encapsulation of behaviour would be the context together with its roles. In SOA, this encapsulation is the service itself. So it is possible to realign the ideas in that white paper, to think of a context as being a service. If the goal is to create a hybrid between services and DCI, so that all of the concerns listed at the start of this article get handled by a hybrid solution, then the roles still exist as they do in DCI, but the context becomes a "service" in the technical sense.
Time for an example. Imagine a train company which owns electrified tracks along which trains run. They have a business partner who provides the electrical energy, who is offering cheaper energy to consumers who can forecast their energy consumption so that the partner can make their production more efficient. To deliver these forecasts, the train company checks the timetable for regular passenger transport, as well as all its cargo contracts for bespoke cargo trips. This allows them to simulate all train trips for a given time period and calcuate the energy requirement. Now, the company is a national one, and has thousands of kilometers of track and hundreds of trains running. They have created detailed maps of the terrain over which their tracks run, so they know the uphill and downhill segments and their power consumption changes. They want to build some simulation software to handle the forecasting. At the same time, there is a non-functional requirement to complete calculations extremely quickly, so rather than making them sequential, a parallel solution is required. Here is the proposed object model:
- Trip: Consists of a Train and a list of TripSegments.
- Train: consists of a list of Locomotives, and a list of Wagons. The assembly of a train comes from the Timetable.
- Locomotive: Has an efficiency, a weight and rolling friction factor.
- Wagon: has a weight and rolling friction factor.
- TripSegment: has a Segment, and the average speed along which the Train will travel on that segment, during its trip.
- Segment: a stretch of track, which has a length, positive hill climbs and negative hill decents. Has a starting station and an end station, which are not relevant to the energy calculations - it is the distance which counts.
- Timetable: a service which provides all Trips for a given time period.
In order to make each trip be calculated in parallel, I have chosen to use a feature of EJB whereby the container creates a new thread and does the calculation on that thread. It means with a simple annotation on a method, I have almost no boiler plate code to write! In order to use that annotation, my context needs to be an EJB, but seeing as the application is running in an enterprise Java application server anyway (so that I can for example have a nice website as a front end), this is no big deal.
So, the architecture of this solutions is: a web request to run the calculation is received which makes a call to my process layer (aka application layer). The process layer calls the timetable to get all trips for the requested period, and for each trip it calls the context to do a calculation in parallel. The process layer waits for all calculations to complete and returns the result to the web layer to present the results to the user. Here is the context:
@Stateless @Context public class ForecastingContext implements ForecastingContextLocal { /** * an asynchrounous method for determining the energy requirements of the given trip. */ @Asynchronous public Future forecastEnergyRequired(Trip trip) { BehaviourInjector bi = new BehaviourInjector(this); //the context simply casts objects into roles, and starts the interaction EnergyConciousTrip t = bi.assignRole(trip, EnergyConciousTrip.class); double energy = t.forecastEnergyRequirements(); return new AsyncResult(energy); } }
The context does nothing special, apart from contain the @Asynchronous
annotation which tells the container that calls to this method need to occur on a new thread. Unlike perhaps more standard DCI, this context has no constructor which takes objects which will play roles, rather the interaction method "forecastEnergyRequired" is passed the object which gets cast into a role. The reason is that I have no control over the instantiation of the context, because it is an EJB, meaning that the container does the instantiation for me!. I have not sub-classed BehaviourInjector (which is an option in my framework), because stateless EJBs can be called by multiple threads at the same time, and BehaviourInjector is not thread safe.
Here are the two roles:
/** * this role is played by a trip so that it is able to * give a prediction about how much energy will be * needed to undertake the trip. */ @Role(contextClass=ForecastingContext.class, implementationClass=EnergyConciousTrip.EnergyConciousTripImpl.class) public interface EnergyConciousTrip { double forecastEnergyRequirements(); //role List getTripSegments(); //data Train getTrain(); //data static class EnergyConciousTripImpl{ //role implementation @CurrentContext private IContext ctx; @Self private EnergyConciousTrip self; /** * energy consists of a longitudinal component (ie along the track due to * aerodynamic forces and rolling resistance) as well as altidudal component * (ie due to climbing or descending). * * E = mgh //ie potential energy for climbing / descending * E = Fd //ie energy required to overcome resistive forces */ public double forecastEnergyRequirements(){ //give the train some scientific behaviour and determine //its total weight and average efficiency ScientificTrain train = ctx.assignRole(self.getTrain(), ScientificTrain.class); double totalWeight = train.getTotalWeightKg(); double avgEff = train.getAverageEfficiency(); //add up all energy per segment double energy = 0.0; for(TripSegment seg : self.getTripSegments()){ double segmentEnergy = train.getResistanceForce(seg.getSpeed()) * seg.getSegment().getLengthMeters(); segmentEnergy += totalWeight * seg.getSegment().getAltitudeChange(); //each locomotive can pull, but has an efficiency which needs to be factored in! //it needs more energy than just pulling wagons, because its inefficient segmentEnergy /= avgEff; energy += segmentEnergy; } return energy; } } } /** * played by a train, when it needs to provide scientific details about itself. */ @Role(contextClass=ForecastingContext.class, implementationClass=ScientificTrain.ScientificTrainImpl.class) public interface ScientificTrain { double getResistanceForce(double speed); //role int getTotalWeightKg(); //role double getAverageEfficiency(); //role List getWagons(); //data List getLocomotives(); //data double getDragCoefficient(); //data static class ScientificTrainImpl { //role impl @Self private ScientificTrain self; /** * resistance force is the rolling friction as a * result of weight, as well as aerodyamic drag. */ public double getResistanceForce(double speed){ double resistanceForce = 0.0; for(Wagon w : self.getWagons()){ resistanceForce += w.getWeightKg() * w.getRollingFriction(); } for(Locomotive l : self.getLocomotives()){ resistanceForce += l.getWeightKg() * l.getRollingFriction(); } resistanceForce += speed * speed * self.getDragCoefficient(); return resistanceForce; } /** total weight of wagons and locs. */ public int getTotalWeightKg(){ int weight = 0; for(Wagon w : self.getWagons()){ weight += w.getWeightKg(); } for(Locomotive l : self.getLocomotives()){ weight += l.getWeightKg(); } return weight; } /** average of all locs in the train */ public double getAverageEfficiency(){ double avg = 0.0; for(Locomotive l : self.getLocomotives()){ avg += l.getEfficiency(); } return avg / self.getLocomotives().size(); } } }
There is nothing fancy in these roles - simply the engineering calculations to work out the energy requirements. I have chosen to implement the role implementations inside the role interface, but there is no reason the role implementations could not exist in their own classes.
Finally, here is the process layer:
@Stateless public class EnergyForecastingService implements EnergyForecastingServiceLocal { @EJB private TimetableLocal timetable; @EJB private ForecastingContextLocal forecastingContext; @Override public EnergyRequirementResult forecast() throws InterruptedException, ExecutionException { long start = System.nanoTime(); List> results = new ArrayList>(); EnergyRequirementResult result = new EnergyRequirementResult(); result.setTrips(timetable.getTripsForPeriod(null, null)); for(Trip trip : result.getTrips()){ //start it in a parallel thread //it will be simulated using a DCI context... Future e = forecastingContext.forecastEnergyRequired(trip); results.add(e); } //ok, all started, lets wait until all are done... result.setEnergyRequirement(waitForResults(results)); result.setCalculationTimeNs(System.nanoTime() - start); return result; } private double waitForResults(List> results) throws InterruptedException, ExecutionException { double energyRequired = 0.0; while(!results.isEmpty()){ List> assimilated = new ArrayList>(); for(Future result : results){ if(result.isDone()){ assimilated.add(result); energyRequired += result.get().doubleValue(); } } results.removeAll(assimilated); //lets give CPU time to other threads while we wait... Thread.sleep(5L); } return energyRequired; } }
The process layer uses these funny "Future" objects which allow it to not only get the result from a calculation which has run on another thread, but also to query whether that thread has completed or is still running. The only boiler plate code needed to handle this multi-threaded code is analysis of results while waiting for them all to complete.
The context and its roles look like this:
One interesting side point, is that the EnergyRequirementResult is a class in the context's package (namespace), because it only makes sense to create it from the context. It can be used outside of the context as a read only object to read the results, but it is strongly coupled to the context. It doesn't really make sense for it to exist without the context, and thinking in terms of components, or code re-use, it makes sense to place it in the same namespace as the context. The context and its roles, are afterall a component which can calculate energy requirements - they encapsulate the behaviour to do this.
It might look strange that the behaviour is not simply added to the train and trip objects. But it's hard to see the benefits when looking at such a small project. Imagine a huge project where the object model is much more complex, and there are many many more use cases. If you look at the object model in this example, the train and timetable can be used in many more applications than just one which does energy predictions. Typically within an enterprise, we keep rebuilding similar object models with relevant parts of data, and differing behaviours for each OO solution. DCI lets you build a large single object model, with behaviours constrained to the contexts in which they are relevant. This allows the data model to become sufficiently complex to make it usable within the entire enterprise, but not so overly complex that making changes to it breaks it. Of course it is also arguable, that things like drag coefficients and rolling friction coefficients have no place in an object model of an application related to passenger load planning or whatever else the enterprise needs to do. In terms of making applications flexible to change, it can help if each application has its own object model, even if it results in code duplication within the enterprise.
In this example I have turned my context into an EJB, because I want to take advantage of boilerplate code which the container can handle for me - in this case concurrent computing. Similarly, if my context needs resources, transactions, security, etc, I would use an EJB for my context, and pass the resources which the container injects into it, into the roles using my BehaviourInjector framework (see its documentation for details). I have solved the banking example used in many DCI articles, by making the contexts EJBs, letting the container handle transactions, and getting hold of the EntityManager (for interacting with the database) by letting the container inject it into the context, which the BehaviourInjector can in turn inject into roles.
If contexts are to be "services" in the technical sense, it also makes sense for an enterprise to build a context repository to sit along side their service repository. These are places for software people to look, when needing to see if problems they face have already been solved. Just as we have tools for searching service repositories and we create documents to list all our services, we will need to do the same for contexts, if DCI is to really be adopted by the enterprise world.
It's important to remember that you could build this solution with pure services, or pure OO. DCI is simply another, and valid way of solving the problem, which concentrates on separating the behaviour from the data in order to make the code more reviewable, while at the same time remaining object oriented, so that the model which the users and customers have, stays similar to that which the programmer uses. Is creating roles and injecting behaviour into the Train and Trip objects better than putting those methods into a service which does the same thing? I don't know... It is certainly less OO, but is that a bad thing? Perhaps its just down to designers choice and feeling comfortable with the resulting code. So long as reading the solution compares to the users and customers stories, that is the important thing. Mappings between the users/customers world, and the programmers world are unnecessary and cause bugs as well as unsatisfactory users, because the programmer concentrates on solving his problems, rather than the business' problems.
You can download all the source code for this train app from here.
PS. I have no idea if train companies need to forecast energy requirements, or even if they work with energy partners. The creativity of this blog article should not hamper the readers understanding of how to create a service/DCI hybrid, in order to benefit from the way a container handles the concerns listed at the start of this article!
发表评论
-
论继承
2011-09-05 17:37 218继承是现实系统中非常 ... -
Jdon007四象图坐标表示
2011-09-03 11:42 1626http://www.jdon.com/jivejdon/th ... -
面向对象掠影
2011-07-16 22:25 828转自链接:http://www.cnblo ... -
specification 规格模式
2011-02-14 22:57 115MF 和 Evans 发明的模式 -
A comparison of DCI and SOA, in Java_003
2011-02-14 12:43 62DCI和SOA的对照 -
DCI architecture - The Common Sense of Object Orientated Programming
2011-02-14 12:38 77In regular OO, I work with cla ... -
MVC To DCI
2011-02-14 12:29 204MVC to DCI -
DCI in Real World
2011-02-14 12:22 826逃出面向类编程的魔爪,重新思考对象 This is a ... -
DCI之转账简单Example
2011-02-14 11:33 194http://stackoverflow.com/questi ... -
The DCI Architecture
2011-02-14 01:05 1157The DCI Architecture: A ... -
为关系数据库设计对象
2011-02-12 20:36 958这是DDD的原文,我认为最好的结论就是最后加粗 ... -
DDD 概念
2011-02-11 15:22 775来自一个PPT的截图 ... -
UML元素
2011-02-11 14:52 659UML 统一建模语言,它是表达我们OO建模的图形工具,UML图 ... -
Rethinking architecture with CQRS
2011-02-10 22:57 911这是axonframework的作者Allard Buijze ... -
来自Jdon的DDD总结
2011-02-10 22:31 929http://www.jdon.com/jivejdon/th ... -
DDD设计,为什么我热爱CQRS
2011-02-10 22:25 1491地址是:http://jonathan-oliver.bl ... -
设计模式的脉络
2011-02-10 14:13 721设计模式一般是指GOF那本书引出来的名词,其应该是代码模式,而 ... -
对象设计原则
2011-02-08 00:36 711现在我们面对的是让人 ...
相关推荐
标题中的“GD32F407 DCI LWIP”涉及到的是基于兆易创新(GigaDevice)的GD32F407系列单片机,利用DCI(Digital Camera Interface,数字摄像头接口)功能,并集成轻量级网络协议栈LWIP(Lightweight IP)的开发实践。...
DDD还引入了DCI(Data Context and Interaction)架构,它强调数据、上下文和交互的分离。在DCI中,数据对象(如领域模型)在特定的上下文中扮演角色,并通过交互来执行业务逻辑。这种方法使得代码更贴近业务语境,...
MPLS L3vrf DCI配置 https://editor.csdn.net/md/?articleId=120409937 DCI互联 Layer 3 VRF-MPLS (EVE vqfx实验)全部配置
**DM8 DCI编程指南** **一、DM DCI简介** DM8 DCI(Data Communication Interface)是达梦数据库提供的一种编程接口,用于在应用程序和DM数据库之间建立通信,实现数据的存取和处理。DCI接口基于OCI(Oracle Call ...
在中国电信最近的动态中,其对DCI波分设备的采购标志着通信行业的重大进展,预示着开放光网络新时代的开启。DCI(数据中心互联)波分设备是现代通信基础设施的关键组成部分,它在大规模数据传输和云计算服务中扮演着...
4G、5G网络优化
包含DCI0-0、DCI0-1、DCI1-0、DCI1-1的解码 输入字节流,解出对应字段的内容 如0-0中。 Nrb_dl_bwp 48 payload bitstring 11010100101110101111111 频域资源分配 11 11010100101 时域资源分配" 4 1101 VRB-to-PRB...
数据中心互连(DCI)正是为了解决这一需求而产生的技术,其在数据中心的部署规模扩展和服务地理区域铺开中扮演了重要角色。 ### 数据中心面临的主要挑战 随着数据量的急剧增加,数据中心面临的挑战可以概括为以下...
本文档基于中国电信ChinaNet网的网络现状,并根据中国电信宽带互联网业务承载的需求,对ChinaNet网组网结构、路由策略、链路组织、网络安全、资源分配等内容进行了规范。
DCI型细水口模架是模具设计中的一个重要概念,尤其在注塑模具领域中广泛应用。这种模架设计主要用于实现高效的塑料制品生产,确保产品精度和质量。在深入理解DCI型细水口模架之前,我们需要先了解模架的基本结构和...
GY-906 MLX90614ESF BAA BCC DCI IR红外测温传感器模块是一款广泛应用在温度监测领域的高科技设备。这款模块基于Melexis公司的MLX90614ESF芯片,它是一款高精度、非接触式的红外热电堆传感器,能够测量环境及物体...
九、打包和发布文件(Wrapping And Distribution Files) DCP打包涉及到将音频、视频和元数据组合成一个可播放的文件包。这个过程通常使用专门的工具完成,确保内容的完整性和安全性。打包后的DCP文件通过加密传输到...
6645G下行DCI(Downlink Control Information)设计与流程是5G通信系统中的关键技术之一,它在实现高效、灵活的无线资源管理中起着至关重要的作用。本资料整理自一个PDF文档,旨在深入解析6645G网络中的DCI结构、...
在LTE(长期演进)系统中,PDCCH(物理下行控制信道)承载了DCI(下行控制信息),这是核心网络与用户设备之间通信的关键组成部分。DCI用于指示资源分配、调度信息、功率控制命令等,使得UE(用户设备)能够正确解码...