- 浏览: 220991 次
- 性别:
- 来自: 深圳
最新评论
-
xqying90:
啥子哟~
div屏幕居中的方法 -
ydc919:
JavaBeansDataExchange could not instantiate result class -
yongtree:
也太不全了啊,js呢
jquery选中单选框、复选框、下拉框 -
awindbird:
希望好用。
android ERROR: unknown virtual device name -
fangwei:
xgj1988 写道 有何高见
div屏幕居中的方法
本文所讨论的话题
通常在一个业务系统中会有各种不同的角色,而在系统的若干功能模块中,这些角色所能看到的数据是不一样的。那么在程序中如何处理类似问题会更优呢,本文想通过一个简单的场景来和大家再次探讨一下如何用OO来改善我们的系统。
场景
系统中目前有三种角色,超级管理员、管理员、普通用户。有一个功能是显示书籍列表,每一种角色所能看到的书籍是不同的。这里面会有一些规则,但是这些规则不是文本所要讨论的重点。
最初的实现
通常我们最快能想到的思路是,先创建两个类,用户类User、书籍类Book。在User类中我们创建了判断用户角色类型的方法。
package fangwei.solution1.user.domain; public class User { public static final int ROLE_SUPER_ADMIN = 1; public static final int ROLE_ADMIN = 2; public static final int ROLE_COMMON_USER = 3; private Integer roleId; public void setRoleId(Integer roleId) { this.roleId = roleId; } public Integer getRoleId() { return roleId; } public boolean isSuperAdmin() { return this.roleId==ROLE_SUPER_ADMIN; } public boolean isAdmin() { return this.roleId==ROLE_ADMIN; } public boolean isCommonUser() { return this.roleId==ROLE_COMMON_USER; } }
package fangwei.solution1.book.domain; public class Book { }
然后用分层的方式去处理问题,先创建service层的接口。为了处理不同角色的情况,我们传入了一个user对象。
package fangwei.solution1.book.service; import java.util.List; import fangwei.solution1.book.domain.Book; import fangwei.solution1.user.domain.User; public interface BookService { public List<Book> listBook(User user); }
实现service层的接口,同时我们需要创建dao层的接口
package fangwei.solution1.book.service; import java.util.List; import fangwei.solution1.book.dao.BookDao; import fangwei.solution1.book.domain.Book; import fangwei.solution1.user.domain.User; public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public List<Book> listBook(User user){ List<Book> result = null; if(user.isSuperAdmin()){ result = bookDao.selectBookList4SuperAdmin(); }else if(user.isAdmin()){ result = bookDao.selectBookList4Admin(); }else if (user.isCommonUser()) { result = bookDao.selectBookList4CommonUser(); } return result; } }
package fangwei.solution1.book.dao; import java.util.List; import fangwei.solution1.book.domain.Book; public interface BookDao { public List<Book> selectBookList4SuperAdmin(); public List<Book> selectBookList4Admin(); public List<Book> selectBookList4CommonUser(); }
实现dao层的接口,为了本文的讨论,我们加入了log语句,而并没有真正去编写实现代码
package fangwei.solution1.book.dao; import org.apache.log4j.Logger; import java.util.List; import fangwei.solution1.book.domain.Book; public class BookDaoImpl implements BookDao { private static final Logger logger = Logger.getLogger(BookDaoImpl.class); public List<Book> selectBookList4SuperAdmin() { logger.debug("selectBookList4SuperAdmin"); return null; } public List<Book> selectBookList4Admin() { logger.debug("selectBookList4Admin"); return null; } public List<Book> selectBookList4CommonUser() { logger.debug("selectBookList4CommonUser"); return null; } }
好了,下面我们来编写一个测试,来看service层的实现是否正确
package fangwei.solution1.book.service; import org.junit.After; import org.junit.Before; import org.junit.Test; import fangwei.solution1.book.dao.BookDaoImpl; import fangwei.solution1.book.service.BookServiceImpl; import fangwei.solution1.user.domain.User; public class TestBookServiceImpl { private BookServiceImpl bookService; @Before public void setUp() throws Exception { bookService = new BookServiceImpl(); bookService.setBookDao(new BookDaoImpl()); } @After public void tearDown() throws Exception { } @Test public void testListBook() { User user = new User(); user.setRoleId(User.ROLE_SUPER_ADMIN); bookService.listBook(user); user.setRoleId(User.ROLE_ADMIN); bookService.listBook(user); user.setRoleId(User.ROLE_COMMON_USER); bookService.listBook(user); } }
运行这个测试,输出结果如下
selectBookList4SuperAdmin selectBookList4Admin selectBookList4CommonUser
说明这个实现是正确的。
问题在哪里
在一个业务系统中类似上面的场景会有很多,也就是说我们会在service层的若干实现方法中使用if...else if...else if...用来处理不同角色的情况。如果角色类型是不会增加的,那么上面的实现没有任何问题。但是很可惜,客户在使用系统一段时间后提出要增加新的角色类型,当然新角色所能看到的书籍列表也是有别于之前角色的:(
这个时候,我们痛苦的发现,我们需要满系统去找使用if...else if...else if...用来处理不同角色的地方,然后再加入一个else if。有人会说,这没什么啊,我们的团队是按功能模块划分来开发的,每个人挨个service类去加就好了。但是我当时的想法是,一定有比这更好的设计。
另一种实现
我们再回过头去看service层的listBook方法,其实我们要做的事只有一件——查询书籍列表,只是因为角色这个因素导致出现了分支结构。那么我们可以将这个变化抽象出来,使我们在查询书籍列表时不需要关心角色这个因素。下面是另一种实现的service层接口,同时我们抽象出了新的接口BookBiz
package fangwei.solution2.book.service; import java.util.List; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public interface BookService { public List<Book> listBook(BookBiz bookBiz); }
package fangwei.solution2.book.domain; import java.util.List; import fangwei.solution2.book.domain.Book; public interface BookBiz { public List<Book> listBook(); }
这样,我们在实现service层接口的时候就不需要考虑角色因素了
package fangwei.solution2.book.service; import java.util.List; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class BookServiceImpl implements BookService { public List<Book> listBook(BookBiz bookBiz) { return bookBiz.listBook(); } }
那么我们如何使用BookBiz这个接口呢,我们可以创建三个新的用户类分别对应于场景中三种不同的角色,然后让他们实现BookBiz这个接口。为了测试的简单,我们直接依赖了dao层的实现BookDaoImpl,在实际当中可以使用spring等ioc容器在运行期注入。
package fangwei.solution2.user.domain; import java.util.List; import fangwei.solution2.book.dao.BookDao; import fangwei.solution2.book.dao.BookDaoImpl; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class SuperAdminUser extends User implements BookBiz { BookDao bookDao = new BookDaoImpl(); public List<Book> listBook() { return bookDao.selectBookList4SuperAdmin(); } }
package fangwei.solution2.user.domain; import java.util.List; import fangwei.solution2.book.dao.BookDao; import fangwei.solution2.book.dao.BookDaoImpl; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class AdminUser extends User implements BookBiz { BookDao bookDao = new BookDaoImpl(); public List<Book> listBook() { return bookDao.selectBookList4Admin(); } }
package fangwei.solution2.user.domain; import java.util.List; import fangwei.solution2.book.dao.BookDao; import fangwei.solution2.book.dao.BookDaoImpl; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class CommonUser extends User implements BookBiz { BookDao bookDao = new BookDaoImpl(); public List<Book> listBook() { return bookDao.selectBookList4CommonUser(); } }
依然需要写一个测试来验证我们的设计
package fangwei.solution2.book.service; import static org.junit.Assert.*; import org.junit.After; import org.junit.Before; import org.junit.Test; import fangwei.solution2.book.domain.BookBiz; import fangwei.solution2.book.service.BookService; import fangwei.solution2.book.service.BookServiceImpl; import fangwei.solution2.user.domain.AdminUser; import fangwei.solution2.user.domain.CommonUser; import fangwei.solution2.user.domain.SuperAdminUser; public class TestBookServiceImpl { private BookService bookService; @Before public void setUp() throws Exception { bookService = new BookServiceImpl(); } @After public void tearDown() throws Exception { } @Test public void testListBook() { BookBiz bookBiz = new SuperAdminUser(); bookService.listBook(bookBiz); bookBiz = new AdminUser(); bookService.listBook(bookBiz); bookBiz = new CommonUser(); bookService.listBook(bookBiz); } }
运行测试的输出结果同第一种实现
selectBookList4SuperAdmin selectBookList4Admin selectBookList4CommonUser
上层如何调用
通常我们都会将User对象放入HttpSession对象中,所以要使用此种实现,我们需要在用户登录成功后,根据不同的角色创建不同的用户类放入HttpSession对象中。登录的代码在这里就略过了,下面给出场景的一种action层实现及测试代码,重点是对HttpSession对象的操作,框架是次要因素
package fangwei.solution2.book.action; import java.util.List; import java.util.Map; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Result; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionSupport; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; import fangwei.solution2.book.service.BookService; public class BookAction extends ActionSupport implements SessionAware{ private static final long serialVersionUID = 2744158510001123482L; private List<Book> bookList; private Map<String, Object> session; private BookService bookService; public void setBookList(List<Book> bookList) { this.bookList = bookList; } public List<Book> getBookList() { return bookList; } public void setSession(Map<String, Object> session) { this.session = session; } public void setBookService(BookService bookService) { this.bookService = bookService; } @Action(value="/book/listBook", results = {@Result(name=SUCCESS,location="/WEB-INF/jsp/book/listBook.jsp")} ) public void listBook() { BookBiz bookBiz = (BookBiz) session.get("user"); bookList = bookService.listBook(bookBiz); } }
package fangwei.solution2.book.action; import static org.junit.Assert.*; import java.util.HashMap; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; import fangwei.solution2.book.service.BookServiceImpl; import fangwei.solution2.user.domain.AdminUser; import fangwei.solution2.user.domain.CommonUser; import fangwei.solution2.user.domain.SuperAdminUser; import fangwei.solution2.user.domain.User; public class TestBookAction { private BookAction bookAction; @Before public void setUp() throws Exception { bookAction = new BookAction(); bookAction.setBookService(new BookServiceImpl()); } @After public void tearDown() throws Exception { } @Test public void testListBook() { Map<String, Object> session = new HashMap<String, Object>(); bookAction.setSession(session); User user = new SuperAdminUser(); session.put("user", user); bookAction.listBook(); user = new AdminUser(); session.put("user", user); bookAction.listBook(); user = new CommonUser(); session.put("user", user); bookAction.listBook(); } }
好处在哪里
再回到第一种实现提出的问题,现在如果我们需要增加新的角色类型,就可以创建一个新的用户类XXXUser,然后实现BookBiz等需要的业务接口就可以了。在这种设计下,我们再也不需要满系统去增加else if了。同样的一句代码bookService.listBook(bookBiz);在运行期会有不同的执行效果,这就是OO的多态之一。有人会说,看起来service层的实现没啥用了,只是一个facade了。其实不然,BookBiz抽象出来的是由于角色因素导致的变化,我们依然可以在service中编写所有角色共有的后续业务逻辑,而不必在每个BookBiz的实现中去重复。
package fangwei.solution2.book.service; import java.util.List; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class BookServiceImpl implements BookService { public List<Book> listBook(BookBiz bookBiz) { List<Book> listBook = bookBiz.listBook(); //后续的业务逻辑 //... return listBook; } }
疑惑依然存在
第二种实现隔离了由于角色因素导致的变化,使我们能够更方便的增加新的角色类型,但是当需要隔离的变化多了以后会怎么样呢。我们的BookBiz接口中的方法会越来越多,类似BookBiz的接口会越来越多,但是我们的每一种用户类如SuperAdminUser只有一个,那么最后我们的SuperAdminUser类实现的接口会越来越多,即实现的接口方法会越来越多,这个类会不断的膨胀下去导致维护困难。。。这种情况,我们又该如何应对呢?一定还有更优的设计?本文抛砖引玉,旨在了解大家在遇到类似问题时如何用OO的思想去分析解决。
评论
特地跑去回顾了一下策略模式,感觉有些相似,策略模式多半使用抽象类+在子类中覆盖抽象类方法实现。
我个人比较喜欢折腾
发表评论
-
Java security KeyStore Cipher
2011-12-23 07:42 1297http://docs.oracle.com/javase/7 ... -
DirectInfo.GetFiles 排序
2011-10-19 15:50 1982The order in which this funct ... -
linux 实时观察文件行数变化
2011-09-16 17:48 2009#watch --interval=1 wc -l 20110 ... -
org.apache.poi 读取 excel xls xlsx
2011-08-30 09:56 8566http://poi.apache.org/download. ... -
mysql 列转行 GROUP_CONCAT
2011-08-09 18:36 2980GROUP_CONCAT(expr) 该函数返回 ... -
MMS 多媒体短信服务 彩信
2011-06-02 14:54 1294MMS为Multimedia Messaging Servic ... -
SMS的体系结构
2011-06-02 14:29 1543GSM标准中定义的点-点短消息服务使得短消息能在移动台和短消息 ... -
C# 事件 EventHanlder
2011-05-25 11:26 998using System; public class ... -
linux shell 根据目录拼出 java classpath
2011-03-28 11:21 1151#ls /demo/lib a.jar b.jar ... -
apache resin 端口关联
2011-03-13 15:24 920/etc/apache2/httpd.conf ResinC ... -
ubuntu 用户相关
2011-03-12 10:06 0修改用户默认组 usermod -g sms fangwei ... -
传递带空格的参数给linux shell中的java命令
2011-01-14 17:23 3733比如说 $ test.sh "2011-01-01 ... -
ubuntu server版配置关闭系统自动更新
2011-01-11 16:37 4244修改/etc/apt/apt.conf.d/50unat ... -
tomcat配置https ssl
2010-12-14 22:09 1067生成证书文件.keystore E:\>$JAVA_H ... -
junit4定义测试集TestSuite Declaration
2009-09-11 12:26 4915转载自 Joe Ocampo http://ww ... -
jquery选中单选框、复选框、下拉框
2009-09-10 14:39 6273转载自 jquery1.3中文参考 http:// ... -
使用<!-- //-->这样的html注释把js代码注起来的作用
2009-09-02 22:18 4813一直不明白用eclipse代码提示功能生成<script ... -
log4j布局PatternLayout详细手册
2009-08-22 10:02 1773转载自 log4j官网 http://logging. ... -
Runtime.getRuntime().exec(cmd)的超时处理
2009-08-17 18:22 4110在使用Runtime.getRuntime().exec(cm ... -
div屏幕居中的方法
2009-08-07 22:28 22469<style type="text/css&q ...
相关推荐
相较于结构化编程,面向对象编程可以更好地模拟真实世界中的问题,使得代码更具可读性、可维护性和可扩展性。同时,面向对象编程也更加符合人类的思考方式,更易于学习和理解。 不过,结构化编程和面向对象编程并非...
结构化程序设计与面向对象程序设计的简述全文共4页,当前为第1页。结构化程序设计与面向对象... 程序员采用结构化编程方法,将一个复杂的程序分解成若干个子结构,便于控制、降低程序的复杂性,因此容易编写程序,同
模块是结构化编程的基本单位,但计算方法(简称为算法)是程序的核心。沃尔森曾提出了一个公式:程序=算法+数据结构。结构化方法只是对传统程序结构的改进,用三种基本结构来组织程序,使程序结构更为清晰,程序开发更...
本文将会用到西门子的Step7编程语言和施奈德的Unity 编程语言来讲解PLC的面向对象编程。
本章首先简要介绍了结构化的软件开发过程,然后介绍面向对象的软件开发过程,对面向对象的一些核心思想和概念做了阐述。本章列举了不少形象的例子,来帮助读者理解面向对象的开发思想,并且以一个画板Panel软件系统...
理解结构化程序设计_理解面向对象(共25页).ppt 推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 03.数据类型和运算符(共19页).ppt 推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 04.流程控制和...
孙卫琴老师的书中,内容都是自己深刻思考过,并通过通俗易懂的语言表述出来,最大限度的易懂,易学,这是一本适合新手入门,掌握良好java开发习惯,开发思想的好书,希望大家多多从简单的示例中体会java的面向对象的...
孙卫琴老师的书中,内容都是自己深刻思考过,并通过通俗易懂的语言表述出来,最大限度的易懂,易学,这是一本适合新手入门,掌握良好java开发习惯,开发思想的好书,希望大家多多从简单的示例中体会java的面向对象的...
孙卫琴老师的书中,内容都是自己深刻思考过,并通过通俗易懂的语言表述出来,最大限度的易懂,易学,这是一本适合新手入门,掌握良好java开发习惯,开发思想的好书,希望大家多多从简单的示例中体会java的面向对象的...
资源描述:Java面向对象知识点的思维导图 内容概要: 这个资源是一个Java面向对象知识点的思维导图,它涵盖了Java中面向对象编程的...这个导图以可视化的方式呈现了Java面向对象编程的知识点,结构清晰、易于理解。
结构化编程的语言主流的是c语言,采用结构化的编程方式,主要用来编写操作系统。特点:语言灵活非常接近底层,对硬件有强大的访问能力。对于一个比较复杂的系统,往往是自顶向下,逐步求精,分模块的思想来编写。...
面向过程与面向对象 6.1.1 面向过程与面向对象 1、概念 面向过程(Procedure Oriented)也可称之为“面向记录”,是一种以过程为中心的编程思想。它注重的是具体的步骤,只有按照步骤一步一步执行,才能够完成某件...
其实它在以前基本被叫做“结构化编程”。 早期的程序设计,大量使用共享变量(全局变量)和GOTO语句一类的东西,后来有人证明所有有意义的程序流程都可以使用三种基本流程(顺序、选择、重复)来实现,并提出“GOTO...
C# 是一个现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。 C# 是由 Anders Hejlsberg 和他的团队在 .Net 框架开发期间开发的。 C# 是专为公共语言基础结构(CLI...
⾯向对象编程(Object-oriented Programming,简称OOP)是⼀种编程范例,它提供了⼀种结构化程序的⽅法,以便将属性和⾏为捆绑 到单个对象中。 例如,对象可以表⽰具有姓名属性,年龄,地址等的⼈,具有⾏⾛,说话...
面向对象编程初级 类和对象 包 包对象 面向对象编程中级 封装 继承 多态 面向对象编程高级 伴生对象 特质 嵌套类 类型约束 Scala数据结构之集合 函数式编程高级 隐式转换和隐式值 偏函数 高阶函数 ...
资源描述:Java面向对象知识点的思维导图 内容概要: 这个资源是一个Java面向对象知识点的思维导图,它涵盖了Java中面向对象编程的...这个导图以可视化的方式呈现了Java面向对象编程的知识点,结构清晰、易于理解。
孙卫琴老师的书中,内容都是自己深刻思考过,并通过通俗易懂的语言表述出来,最大限度的易懂,易学,这是一本适合新手入门,掌握良好java开发习惯,开发思想的好书,希望大家多多从简单的示例中体会java的面向对象的...
到目前为止,面向对象技术已是软件开发 的主流,全面取代了结构化编程技术曾经具有的地位。 面向对象技术与结构化编程技术有着不同的风格,但同时也有着密切的联系。从具体编 程角度来看,面向对象技术与结构化编程...
Visual Basic是一种由 Microsoft 公司开发的结构化的、模块化的、面向对象的、包含协助开发环境的事件驱动为机制的可视化程序设计语言。从任何标准来说,VB都是世界上使用人数最多的语言