首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

rubby跟java 充血与贫血模型比较

2012-08-22 
rubby和java 充血与贫血模型比较domain model,又称为领域模型,是Java企业应用讨论的一个热门话题,JavaEye

rubby和java 充血与贫血模型比较

domain model,又称为领域模型,是Java企业应用讨论的一个热门话题,JavaEye也曾经多次围绕这个话题讨论,我们来看个简单的例子:

    class User < ActiveRecord::Base has_and_belongs_to_many :kinds has_many :tasks, :dependent => :destroy do def processing_tasks find :all, :conditions => ["start_time <= ? AND end_time is null", Time.now] end end def apply_task(task_name) self.tasks << Task.new(:name => task_name, :start_time => Date.today) end def self.all_processing_tasks Task.find :all, :conditions => ["start_time <= ? AND end_time is null AND user_id is not null",Time.now] endendclass Task < ActiveRecord::Base belongs_to : owner, :class_name => 'User', :foreign_key => 'user_id' belongs_to :kind def self.current_month_tasks(kind) kind.tasks.current_month_tasks endendclass Kind < ActiveRecord::Base has_and_belongs_to_many :users has_many :tasks do def current_month_tasks month_begin = Date.today - Date.today.mday + 1 month_end = Date.today - Date.today.mday + 30 processing_tasks = find :all, :conditions => ["start_time <= ? AND end_time is null ", month_begin] processed_tasks = find :all, :conditions => ["end_time >= ? AND end_time <= ? ", month_begin, month_end] all_tasks = processing_tasks.clone all_tasks << processed_tasks unless processed_tasks.size == 0 return all_tasks, processed_tasks, processing_tasks end end def add_batch_task_to_users(task_name) self.users.each do |user| task = Task.new(:name => task_name, :start_time => Date.today) user.tasks << task self.tasks << task end endendclass Department def self.employee(username, department) User.create(:name => username, :department => department) end def self.total_salary(department) User.sum :salary, :conditions => ["department = ?", department] endend



    1、某部门录用一名新员工

      Department.employee("robbin","开发部")


      2、某部门员工总薪水总和

        Department.total_salary("开发部")


        3、某员工已经开始但尚未结束的任务

          user.tasks.processing_tasks


          4、给某员工分配一项任务

            user.apply_task("学习Java")


            5、所有用户当前已经开始但尚未结束的任务

              User.all_processing_tasks


              6、对某一类别,给所有和此一类别相关的员工,批量新增一批任务

                kind.add_batch_task_to_users("学习单元测试")


                7、针对任务的统计功能,给定某类别,统计当月总的任务数,已完成任务数,未完成任务数

                  Task.current_month_tasks(kind)



                  这里值得注意的是,RoR可以很方便的采用充血的领域模型,所有的业务逻辑都可以放在相关的domain model里面。这里的user,task和kind都是对应于数据库表的领域模型,而department是不对应数据库的纯业务逻辑的domain model。总共4个ruby文件,4个domain model,55行代码,所有要写的代码都在这里了,代码量确实非常少,每个domain model的颗粒度都比较大。

                  然后我们再看看如何用Java:

                    public class User { private Long id; private String name; private String password; private String gender; private String department; private int salary = 0; private List<Task> tasks = new ArrayList<Task>(); # omit getter/setter methods ......}# omit User's ORM Mapping filepublic class Task { private Long id; private String name; private int duration = 0; private User owner; # omit getter/setter methods ......}# omit Task's ORM Mapping filepublic class Kind { ......}# omit Kind's ORM Mapping filepublic interface UserDao { public void addUser(User user); public loadUserById(Long id); # omit CRUD and other persistent methods ...... public List<User> findByDeparment(String department);}public interface TaskDao { # omit CRUD and other persistent methods ......}public class UserDaoImpl { # omit implementations ......}public class TaskDaoImpl { # omit implementations ......}public class UserService { private UserDao userDao; public setUserDao(UserDao userDao) { this.userDao = userDao; } public int workload(User user) { int totalDuration = 0; for (Task task : user.getTasks()) { totalDuration += task.duration; } return totalDuration; } public employee(String username, String department) { User user = new User(); user.setName(username); user.setDepartment(department); userDao.addUser(user); }}public class TaskService { private TaskDao taskDao; public void setTaskDao(TaskDao taskDao) { this.taskDao = taskDao } public applyTask(String taskName, User user) { Task task = new Task(); task.setName(taskName); task.setUser(user); taskDao.addTask(task); }}public class DepartmentService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public int totalSalary(String department) { ...... } ...... } # omit IoC Container weaving configuration's file



                    Java版本的实现代码大家都比较熟悉,因此绝大部分代码都省略了。Java版本需要3个持久对象,3个映射XML文件,3个DAO接口和实现类,4个Service和实现类,和一个IoC的bean组装文件,总共21个文件,全部逻辑写完整,代码行数至少上千行。

                    通过对比,我们可以看到Java比较流行的实现是贫血的模型,按照面向对象的基本原则,对象的状态应该和它的行为封装在一起,因此Java多出来的这些XXXService是一些从纯理论角度而言应该放入其相应的持久对象中去。但是Java实现充血模型从技术上有一定的难度,如何Service方法挪入到持久对象中呢?如何解决Dao的注入问题?如何解决domain logic方法的事务封装问题?前者可以通过AspectJ的静态织入来解决,后者也许可以通过织入或者annotation声明来解决。但不管怎么说,Java从技术上很难实现充血模型,而且即使实现充血模型,也会导致一个Java类好几百行代码的状况,其代码的可阅读性,模块解藕能力都会变得很差,因此我们认为Java不适合充血模型,在表达复杂的业务逻辑的能力上,Java要比ruby差很多:

                    结论:
                    对于Java来说,更加适合采用贫血的模型,Java比较适合于把一个复杂的业务逻辑分离到n个小对象中去,每个小对象描述单一的职责,n个对象互相协作来表达一个复杂的业务逻辑,这n个对象之间的依赖和协作需要通过外部的容器例如IoC来显式的管理。但对于每个具体的对象来说,他们毫无疑问是贫血的。

                    这种贫血的模型好处是:
                    1、每个贫血对象职责单一,所以模块解藕程度很高,有利于错误的隔离。
                    2、非常重要的是,这种模型非常适合于软件外包和大规模软件团队的协作。每个编程个体只需要负责单一职责的小对象模块编写,不会互相影响。

                    贫血模型的坏处是:
                    1、由于对象状态和行为分离,所以一个完整的业务逻辑的描述不能够在一个类当中完成,而是一组互相协作的类共同完成的。因此可复用的颗粒度比较小,代码量膨胀的很厉害,最重要的是业务逻辑的描述能力比较差,一个稍微复杂的业务逻辑,就需要太多类和太多代码去表达(针对我们假定的这个简单的工时管理系统的业务逻辑实现,ruby使用了50行代码,但Java至少要上千行代码)。
                    2、对象协作依赖于外部容器的组装,因此裸写代码是不可能的了,必须借助于外部的IoC容器。

                    对于Ruby来说,更加适合充血模型。因为ruby语言的表达能力非常强大,现在用ruby做企业应用的DSL是一个很热门的领域,DSL说白了就是用来描述某个行业业务逻辑的专用语言。

                    充血模型的好处是:
                    1、对象自洽程度很高,表达能力很强,因此非常适合于复杂的企业业务逻辑的实现,以及可复用程度比较高。
                    2、不必依赖外部容器的组装,所以RoR没有IoC的概念。

                    充血模型的坏处是:
                    1、对象高度自洽的结果是不利于大规模团队分工协作。一个编程个体至少要完成一个完整业务逻辑的功能。对于单个完整业务逻辑,无法再细分下去了。
                    2、随着业务逻辑的变动,领域模型可能会处于比较频繁的变动状态中,领域模型不够稳定也会带来web层代码频繁变动。

                    附件是完整的RoR版本的项目示例代码。要运行它,需要安装MySQL数据库(InnoDB表类型),Ruby和Ruby on rails环境。在MySQL数据库中分别创建demo数据库和demo_test数据库,修改demo\config\database.yml中的MySQL数据库配置,改成你的数据库密码。然后在项目跟目录下面执行:

热点排行