五种Java设计模式透析
来源:互联网 发布:代尔塔劳保鞋淘宝 编辑:程序博客网 时间:2024/05/17 08:26
一、Java设计模式透析之 —— 模板方法(Template Method)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8744002
今天你还是像往常一样来上班,一如既往地开始了你的编程工作。
项目经理告诉你,今天想在服务器端增加一个新功能,希望写一个方法,能对Book对象进行处理,将Book对象的所有字段以XML格式进行包装,这样以后可以方便与客户端进行交互。并且在包装开始前和结束后要打印日志,这样方便调试和问题定位。
没问题!你觉得这个功能简直是小菜一碟,非常自信地开始写起代码。
Book对象代码如下:
- public class Book {
- private String bookName;
- private int pages;
- private double price;
- private String author;
- private String isbn;
- public String getBookName() {
- return bookName;
- }
- public void setBookName(String bookName) {
- this.bookName = bookName;
- }
- public int getPages() {
- return pages;
- }
- public void setPages(int pages) {
- this.pages = pages;
- }
- public double getPrice() {
- return price;
- }
- public void setPrice(double price) {
- this.price = price;
- }
- public String getAuthor() {
- return author;
- }
- public void setAuthor(String author) {
- this.author = author;
- }
- public String getIsbn() {
- return isbn;
- }
- public void setIsbn(String isbn) {
- this.isbn = isbn;
- }
- }
- public class Formatter {
- public String formatBook(Book book) {
- System.out.println("format begins");
- String result = "";
- result += "<book_name>" + book.getBookName() + "</book_name>\n";
- result += "<pages>" + book.getPages() + "</pages>\n";
- result += "<price>" + book.getPrice() + "</price>\n";
- result += "<author>" + book.getAuthor() + "</author>\n";
- result += "<isbn>" + book.getIsbn() + "</isbn>\n";
- System.out.println("format finished");
- return result;
- }
- }
- public class Test {
- public static void main(String[] args) throws Exception {
- Book book = new Book();
- book.setBookName("Thinking in Java");
- book.setPages(880);
- book.setPrice(68);
- book.setAuthor("Bruce Eckel");
- book.setIsbn("9787111213826");
- Formatter formatter = new Formatter();
- String result = formatter.formatBook(book);
- System.out.println(result);
- }
- }
你写好了之后,迫不及待地开始运行,运行结果也完全符合你的期望。
项目经理看完后,对你非常满意,小伙效率很高的嘛!你也非常的得意。
不过两天之后,项目经理又找到了你,他说之前没有考虑到需要交互的客户端还包括手机设备,而手机设备都比较吃流量,用XML格式来传输太耗流量了,想最好能改成使用JSON格式传输。但是之前的XML格式也要保留,最好可以由客户端指定使用哪种格式。
你有些不开心,心里低估着,为什么一开始不考虑周全呢,现在又要改遗留代码。但对方毕竟是领导,你还是要服从命令的,于是你开始修改Formatter类:
- public class Formatter {
- public static final int XML = 0;
- public static final int JSON = 1;
- public String formatBook(Book book, int format) {
- System.out.println("format begins");
- String result = "";
- if (format == XML) {
- result += "<book_name>" + book.getBookName() + "</book_name>\n";
- result += "<pages>" + book.getPages() + "</pages>\n";
- result += "<price>" + book.getPrice() + "</price>\n";
- result += "<author>" + book.getAuthor() + "</author>\n";
- result += "<isbn>" + book.getIsbn() + "</isbn>\n";
- } else if (format == JSON) {
- result += "{\n";
- result += "\"book_name\" : \"" + book.getBookName() + "\",\n";
- result += "\"pages\" : \"" + book.getPages() + "\",\n";
- result += "\"price\" : \"" + book.getPrice() + "\",\n";
- result += "\"author\" : \"" + book.getAuthor() + "\",\n";
- result += "\"isbn\" : \"" + book.getIsbn() + "\",\n";
- result += "}";
- }
- System.out.println("format finished");
- return result;
- }
- }
- public class Test {
- public static void main(String[] args) throws Exception {
- Book book = new Book();
- book.setBookName("Thinking in Java");
- book.setPages(880);
- book.setPrice(68);
- book.setAuthor("Bruce Eckel");
- book.setIsbn("9787111213826");
- Formatter formatter = new Formatter();
- String result = formatter.formatBook(book, Formatter.XML);
- System.out.println(result);
- result = formatter.formatBook(book, Formatter.JSON);
- System.out.println(result);
- }
- }
项目经理看到运行结果后开心地说:“太好了,这正是我想要的!”
可是你这次却没有那么开心,你觉得代码已经有些混乱了,XML格式的逻辑和JSON格式的逻辑混淆在一起,非常不利于阅读,而且如果以后还需要扩展功能也会非常困难。好在传输格式一般也就XML和JSON了,应该不会再有什么扩展了,你这样安慰自己道。
但幻想总会被现实打破,“我最近听说有个YAML格式挺好玩的.......” 项目经理说道。这个时候你已经有想打人的冲动了!!!
很多时候就是这样,在公司里写的代码乱七八糟,质量极差,很大一部分原因就是因为需求变来变去。我们不断在原有代码基础上补充各种后续加入的情况,在一行行新增的if语句下面,我们的代码变得不堪入目。当然,我们作为程序员,对于需求这种东西没有太多的话语权,在这方面我们无能为力。但是我们可以尽量地把程序的架构设计好,让我们写出的代码更具有扩展性,这样就可以应对各种需求变更了。
下面你将要使用23种设计模式中的模板方法来改进以上程序。
首先将Formatter中的代码进行修改,如下所示:
- public abstract class Formatter {
- public String formatBook(Book book, int format) {
- beforeFormat();
- String result = formating(book);
- afterFormat();
- return result;
- }
- protected void beforeFormat() {
- System.out.println("format begins");
- }
- protected abstract String formating(Book book);
- protected void afterFormat() {
- System.out.println("format finished");
- }
- }
然后要定义专门的子类来处理每种传输格式的具体逻辑,这样不同传输格式的逻辑可以从一个方法里分离开,明显便于阅读和理解。
定义类XMLFormatter继承自Formatter,里面加入处理XML格式的具体逻辑:
- public class XMLFormatter extends Formatter {
- @Override
- protected String formating(Book book) {
- String result = "";
- result += "<book_name>" + book.getBookName() + "</book_name>\n";
- result += "<pages>" + book.getPages() + "</pages>\n";
- result += "<price>" + book.getPrice() + "</price>\n";
- result += "<author>" + book.getAuthor() + "</author>\n";
- result += "<isbn>" + book.getIsbn() + "</isbn>\n";
- return result;
- }
- }
- public class JSONFormatter extends Formatter {
- @Override
- protected String formating(Book book) {
- String result = "";
- result += "{\n";
- result += "\"book_name\" : \"" + book.getBookName() + "\",\n";
- result += "\"pages\" : \"" + book.getPages() + "\",\n";
- result += "\"price\" : \"" + book.getPrice() + "\",\n";
- result += "\"author\" : \"" + book.getAuthor() + "\",\n";
- result += "\"isbn\" : \"" + book.getIsbn() + "\",\n";
- result += "}";
- return result;
- }
- }
最后调用代码如下:
- public class Test {
- public static void main(String[] args) throws Exception {
- Book book = new Book();
- book.setBookName("Thinking in Java");
- book.setPages(880);
- book.setPrice(68);
- book.setAuthor("Bruce Eckel");
- book.setIsbn("9787111213826");
- XMLFormatter xmlFormatter = new XMLFormatter();
- String result = xmlFormatter.formatBook(book);
- System.out.println(result);
- JSONFormatter jsonFormatter = new JSONFormatter();
- result = jsonFormatter.formatBook(book);
- System.out.println(result);
- }
- }
定义类YAMLFormatter继承自Formatter,里面加入处理YAML格式的具体逻辑:
- public class YAMLFormatter extends Formatter {
- @Override
- protected String formating(Book book) {
- String result = "";
- result += "book_name: " + book.getBookName() + "\n";
- result += "pages: " + book.getPages() + "\n";
- result += "price: " + book.getPrice() + "\n";
- result += "author: " + book.getAuthor() + "\n";
- result += "isbn: " + book.getIsbn() + "\n";
- return result;
- }
- }
- YAMLFormatter yamlFormatter = new YAMLFormatter();
- String result = yamlFormatter.formatBook(book);
- System.out.println(result);
好了,令人头疼的YAML格式就这样被支持了,只需要在调用的时候决定是实例化XMLFormatter,JSONFormatter还是YAMLFormatter,就可以按照相应的规格进行格式转换了。而且整体的代码很有条理,看起来也很舒心。这个时候,你会轻松地向项目经理调侃一句,还有需要支持的格式吗?
模板方法: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
二、Java设计模式透析之 —— 单例(Singleton)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8860649
写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据。但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像findbugs等代码检查工具还会认为使用System.out.println()是一个bug。
为什么作为Java新手神器的System.out.println(),到了真正项目开发当中会被唾弃呢?其实只要细细分析,你就会发现它的很多弊端。比如不可控制,所有的日志都会在项目上线后照常打印,从而降低运行效率;又或者不能将日志记录到本地文件,一旦打印被清除,日志将再也找不回来;再或者打印的内容没有Tag区分,你将很难辨别这一行日志是在哪个类里打印的。
你的leader也不是傻瓜,用System.out.println()的各项弊端他也清清楚楚,因此他今天给你的任务就是制作一个日志工具类,来提供更好的日志功能。不过你的leader人还不错,并没让你一开始就实现一个具备各项功能的牛逼日志工具类,只需要一个能够控制打印级别的日志工具就好。
这个需求对你来说并不难,你立刻就开始动手编写了,并很快完成了第一个版本:
- public class LogUtil {
- public final int DEGUB = 0;
- public final int INFO = 1;
- public final int ERROR = 2;
- public final int NOTHING = 3;
- public int level = DEGUB;
- public void debug(String msg) {
- if (DEGUB >= level) {
- System.out.println(msg);
- }
- }
- public void info(String msg) {
- if (INFO >= level) {
- System.out.println(msg);
- }
- }
- public void error(String msg) {
- if (ERROR >= level) {
- System.out.println(msg);
- }
- }
- }
- new LogUtil().debug("Hello World");
你迫不及待地将这个工具介绍给你的leader,你的leader听完你的介绍后说:“好样的,今后大伙都用你写的这个工具来打印日志了!”
可是没过多久,你的leader找到你来反馈问题了。他说虽然这个工具好用,可是打印这种事情是不区分对象的,这里每次需要打印日志的时候都需要new出一个新的LogUtil,太占用内存了,希望你可以将这个工具改成用单例模式实现。
你认为你的leader说的很有道理,而且你也正想趁这个机会练习使用一下设计模式,于是你写出了如下的代码:
- public class LogUtil {
- private static LogUtil sLogUtil;
- public final int DEGUB = 0;
- public final int INFO = 1;
- public final int ERROR = 2;
- public final int NOTHING = 3;
- public int level = DEGUB;
- private LogUtil() {
- }
- public static LogUtil getInstance() {
- if (sLogUtil == null) {
- sLogUtil = new LogUtil();
- }
- return sLogUtil;
- }
- public void debug(String msg) {
- if (DEGUB >= level) {
- System.out.println(msg);
- }
- }
- public void info(String msg) {
- if (INFO >= level) {
- System.out.println(msg);
- }
- }
- public void error(String msg) {
- if (ERROR >= level) {
- System.out.println(msg);
- }
- }
- }
- LogUtil.getInstance().debug("Hello World");
你满腹狐疑,单例模式不都是这样实现的吗?还会有什么bug呢?
你的leader提示你,使用单例模式就是为了让这个类在内存中只能有一个实例的,可是你有考虑到在多线程中打印日志的情况吗?如下面代码所示:
- public static LogUtil getInstance() {
- if (sLogUtil == null) {
- sLogUtil = new LogUtil();
- }
- return sLogUtil;
- }
你恍然大悟,不过你的思维非常快,立刻就想到了解决办法,只需要给方法加上同步锁就可以了,代码如下:
- public synchronized static LogUtil getInstance() {
- if (sLogUtil == null) {
- sLogUtil = new LogUtil();
- }
- return sLogUtil;
- }
你的leader看了你的新代码后说:“恩,不错。这确实解决了有可能创建两个实例的情况,但是这段代码还是有问题的。”
你紧张了起来,怎么还会有问题啊?
你的leader笑笑:“不用紧张,这次不是bug,只是性能上可以优化一些。你看一下,如果是在getInstance方法上加了一个synchronized,那么我每次去执行getInstace方法的时候都会受到同步锁的影响,这样运行的效率会降低,其实只需要在第一次创建LogUtil实例的时候加上同步锁就好了。我来教你一下怎么把它优化的更好。”
首先将synchronized关键字从方法声明中去除,把它加入到方法体当中:
- public static LogUtil getInstance() {
- synchronized (LogUtil.class) {
- if (sLogUtil == null) {
- sLogUtil = new LogUtil();
- }
- return sLogUtil;
- }
- }
这样效果是和直接在方法上加synchronized完全一致的。然后在synchronized的外面再加一层判断,如下所示:
- public static LogUtil getInstance() {
- if (sLogUtil == null) {
- synchronized (LogUtil.class) {
- if (sLogUtil == null) {
- sLogUtil = new LogUtil();
- }
- }
- }
- return sLogUtil;
- }
你情不自禁赞叹到,这方法真巧妙啊,能想得出来实在是太聪明了。
你的leader马上谦虚起来:“这种方法叫做双重锁定(Double-Check Locking),可不是我想出来的,更多的资料你可以在网上查一查。”
单例:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
三、Java设计模式透析之 —— 策略(Strategy)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8986285
今天你的leader兴致冲冲地找到你,希望你可以帮他一个小忙,他现在急着要去开会。要帮什么忙呢?你很好奇。
他对你说,当前你们项目的数据库中有一张用户信息表,里面存放了很用户的数据,现在需要完成一个选择性查询用户信息的功能。他说会传递给你一个包含许多用户名的数组,你需要根据这些用户名把他们相应的数据都给查出来。
这个功能很简单的嘛,你爽快地答应了。由于你们项目使用的是MySQL数据库,你很快地写出了如下代码:
- public class QueryUtil {
- public void findUserInfo(String[] usernames) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",
- "123456");
- Statement stat = conn.createStatement();
- StringBuilder sql = new StringBuilder("select * from user_info where ");
- for (String user : usernames) {
- sql.append("username = '");
- sql.append(user);
- sql.append("' or ");
- }
- System.out.println(sql);
- ResultSet resultSet = stat.executeQuery(sql.toString());
- while (resultSet.next()) {
- // 处理从数据库读出来的数据
- }
- // 后面应将读到的数据组装成对象返回,这里略去。
- }
- }
然后,你写了如下代码来测试这个方法:
- public class Test {
- public static void main(String[] args) throws Exception {
- QueryUtil query = new QueryUtil();
- query.findUserInfo(new String[] { "Tom", "Jim", "Anna" });
- }
- }
- select * from user_info where username = 'Tom' or username = 'Jim' or username = 'Anna' or
这可怎么办呢?
有了!你灵光一闪,想出了一个解决办法。等SQL语句拼装完成后,把最后一个or删除掉不就好了么。于是你将代码改成如下所示:
- public class QueryUtil {
- public void findUserInfo(String[] usernames) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",
- "123456");
- Statement stat = conn.createStatement();
- StringBuilder sql = new StringBuilder("select * from user_info where ");
- for (String user : usernames) {
- sql.append("username = '");
- sql.append(user);
- sql.append("' or ");
- }
- sql.delete(sql.length() - " or ".length(), sql.length());
- System.out.println(sql);
- ResultSet resultSet = stat.executeQuery(sql.toString());
- while (resultSet.next()) {
- // 处理从数据库读出来的数据
- }
- // 后面应将读到的数据组装成对象返回,这里略去。
- }
- }
使用StringBuilder的delete方法,把最后多余的一个or删除掉了,这样再运行测试代码,一切就正常了,打印的SQL语句如下所示:
- select * from user_info where username = 'Tom' or username = 'Jim' or username = 'Anna'
你的leader开完会后,过来看了下你的成果。总体来说,他还挺满意,但对于你使用的SQL语句拼装算法,他总是感觉有些不对劲,可是又说不上哪里不好。于是他告诉了你另一种拼装SQL语句的算法,让你加入到代码中,但是之前的那种算法也不要删除,先保留着再说,然后他又很忙似的跑开了。于是,你把他刚刚教你的算法加了进去,代码如下所示:
- public class QueryUtil {
- public void findUserInfo(String[] usernames, int strategy) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",
- "123456");
- Statement stat = conn.createStatement();
- StringBuilder sql = new StringBuilder("select * from user_info where ");
- if (strategy == 1) {
- for (String user : usernames) {
- sql.append("username = '");
- sql.append(user);
- sql.append("' or ");
- }
- sql.delete(sql.length() - " or ".length(), sql.length());
- } else if (strategy == 2) {
- boolean needOr = false;
- for (String user : usernames) {
- if (needOr) {
- sql.append(" or ");
- }
- sql.append("username = '");
- sql.append(user);
- sql.append("'");
- needOr = true;
- }
- }
- System.out.println(sql);
- ResultSet resultSet = stat.executeQuery(sql.toString());
- while (resultSet.next()) {
- // 处理从数据库读出来的数据
- }
- // 后面应将读到的数据组装成对象返回,这里略去。
- }
- }
这样测试代码也需要改成如下方式:
- public class Test {
- public static void main(String[] args) throws Exception {
- QueryUtil query = new QueryUtil();
- query.findUserInfo(new String[] { "Tom", "Jim", "Anna" }, 2);
- }
- }
你立刻把你的leader从百忙之中拖了过来,让他检验一下你当前的成果,可是他还是一如既往的挑剔。
“你这样写的话,findUserInfo这个方法的逻辑就太复杂了,非常不利于阅读,也不利于将来的扩展,如果我还有第三第四种算法想加进去,这个方法还能看吗?” 你的leader指点你,遇到这种情况,就要使用策略模式来解决,策略模式的核心思想就是把算法提取出来放到一个独立的对象中。
为了指点你,他不顾自己的百忙,开始教你如何使用策略模式进行优化。
首先定义一个策略接口:
- public interface Strategy {
- String getSQL(String[] usernames);
- }
- public class Strategy1 implements Strategy {
- @Override
- public String getSQL(String[] usernames) {
- StringBuilder sql = new StringBuilder("select * from user_info where ");
- for (String user : usernames) {
- sql.append("username = '");
- sql.append(user);
- sql.append("' or ");
- }
- sql.delete(sql.length() - " or ".length(), sql.length());
- return sql.toString();
- }
- }
- public class Strategy2 implements Strategy {
- @Override
- public String getSQL(String[] usernames) {
- StringBuilder sql = new StringBuilder("select * from user_info where ");
- boolean needOr = false;
- for (String user : usernames) {
- if (needOr) {
- sql.append(" or ");
- }
- sql.append("username = '");
- sql.append(user);
- sql.append("'");
- needOr = true;
- }
- return sql.toString();
- }
- }
- public class QueryUtil {
- public void findUserInfo(String[] usernames, Strategy strategy) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",
- "123456");
- Statement stat = conn.createStatement();
- String sql = strategy.getSQL(usernames);
- System.out.println(sql);
- ResultSet resultSet = stat.executeQuery(sql);
- while (resultSet.next()) {
- // 处理从数据库读出来的数据
- }
- // 后面应将读到的数据组装成对象返回,这里略去。
- }
- }
- public class Test {
- public static void main(String[] args) throws Exception {
- QueryUtil query = new QueryUtil();
- query.findUserInfo(new String[] { "Tom", "Jim", "Anna" }, new Strategy1());
- query.findUserInfo(new String[] { "Jac", "Joe", "Rose" }, new Strategy2());
- }
- }
- select * from user_info where username = 'Tom' or username = 'Jim' or username = 'Anna'
- select * from user_info where username = 'Jac' or username = 'Joe' or username = 'Rose'
使用策略模式修改之后,代码的可读性和扩展性都有了很大的提高,即使以后还需要添加新的算法,你也是手到擒来了!
策略:它定义了算法家庭,分别封装起来。让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
四、Java设计模式透析之 —— 组合(Composite)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153753
听说你们公司最近新推出了一款电子书阅读应用,市场反应很不错,应用里还有图书商城,用户可以在其中随意选购自己喜欢的书籍。你们公司也是对此项目高度重视,加大了投入力度,决定给此应用再增加点功能。
好吧,你也知道你是逃不过此劫了,没过多久你的leader就找到了你。他告诉你目前的应用对每本书的浏览量和销售量做了统计,但现在想增加对每个书籍分类的浏览量和销售量以及所有书籍总的浏览量和销售量做统计的功能,希望你可以来完成这项功能。
领导安排的工作当然是推脱不掉的,你只能硬着头皮上了,不过好在这个功能看起来也不怎么复杂。
你比较喜欢看小说,那么就从小说类的统计功能开始做起吧。首先通过getAllNovels方法可以获取到所有的小说名,然后将小说名传入getBrowseCount方法可以得到该书的浏览量,将小说名传入getSaleCount方法可以得到该书的销售量。你目前只有这几个已知的API可以使用,那么开始动手吧!
- public int getNovelsBrowseCount() {
- int browseCount = 0;
- List<String> allNovels = getAllNovels();
- for (String novel : allNovels) {
- browseCount += getBrowseCount(novel);
- }
- return browseCount;
- }
- public int getNovelsSaleCount() {
- int saleCount = 0;
- List<String> allNovels = getAllNovels();
- for (String novel : allNovels) {
- saleCount += getSaleCount(novel);
- }
- return saleCount;
- }
小说类的统计就完成了,然后你开始做计算机类书籍的统计功能,代码如下所示:
- public int getComputerBooksBrowseCount() {
- int browseCount = 0;
- List<String> allComputerBooks = getAllComputerBooks();
- for (String computerBook : allComputerBooks) {
- browseCount += getBrowseCount(computerBook);
- }
- return browseCount;
- }
- public int getComputerBooksSaleCount() {
- int saleCount = 0;
- List<String> allComputerBooks = getAllComputerBooks();
- for (String computerBook : allComputerBooks) {
- saleCount += getSaleCount(computerBook);
- }
- return saleCount;
- }
现在你才完成了两类书籍的统计功能,后面还有医学类、自然类、历史类、法律类、政治类、哲学类、旅游类、美食类等等等等书籍。你突然意识到了一些问题的严重性,工作量大倒还不算什么,但再这么写下去,你的方法就要爆炸了,这么多的方法让人看都看不过来,别提怎么使用了。
这个时候你只好向你的leader求助了,跟他说明了你的困惑。只见你的leader思考了片刻,然后自信地告诉你,使用组合模式不仅可以轻松消除你的困惑,还能出色地完成功能。
他立刻向你秀起了编码操作,首先定义一个Statistics接口,里面有两个待实现方法:
- public interface Statistics {
- int getBrowseCount();
- int getSalesCount();
- }
- public class NovelStatistics implements Statistics {
- @Override
- public int getBrowseCount() {
- int browseCount = 0;
- List<String> allNovels = getAllNovels();
- for (String novel : allNovels) {
- browseCount += getBrowseCount(novel);
- }
- return browseCount;
- }
- @Override
- public int getSalesCount() {
- int saleCount = 0;
- List<String> allNovels = getAllNovels();
- for (String novel : allNovels) {
- saleCount += getSaleCount(novel);
- }
- return saleCount;
- }
- }
- public class ComputerBookStatistics implements Statistics {
- @Override
- public int getBrowseCount() {
- int browseCount = 0;
- List<String> allComputerBooks = getAllComputerBooks();
- for (String computerBook : allComputerBooks) {
- browseCount += getBrowseCount(computerBook);
- }
- return browseCount;
- }
- @Override
- public int getSalesCount() {
- int saleCount = 0;
- List<String> allComputerBooks = getAllComputerBooks();
- for (String computerBook : allComputerBooks) {
- saleCount += getSaleCount(computerBook);
- }
- return saleCount;
- }
- }
再定义一个MedicalBookStatistics类实现了Statistics接口,用于统计医学类书籍的浏览量和销售量,代码如下如示:
- public class MedicalBookStatistics implements Statistics {
- @Override
- public int getBrowseCount() {
- int browseCount = 0;
- List<String> allMedicalBooks = getAllMedicalBooks();
- for (String medicalBook : allMedicalBooks) {
- browseCount += getBrowseCount(medicalBook);
- }
- return browseCount;
- }
- @Override
- public int getSalesCount() {
- int saleCount = 0;
- List<String> allMedicalBooks = getAllMedicalBooks();
- for (String medicalBook : allMedicalBooks) {
- saleCount += getSaleCount(medicalBook);
- }
- return saleCount;
- }
- }
- public class TechnicalStatistics implements Statistics {
- private List<Statistics> statistics = new ArrayList<Statistics>();
- public TechnicalStatistics() {
- statistics.add(new ComputerBookStatistics());
- statistics.add(new MedicalBookStatistics());
- }
- @Override
- public int getBrowseCount() {
- int browseCount = 0;
- for (Statistics s : statistics) {
- browseCount += s.getBrowseCount();
- }
- return browseCount;
- }
- @Override
- public int getSalesCount() {
- int saleCount = 0;
- for (Statistics s : statistics) {
- saleCount += s.getBrowseCount();
- }
- return saleCount;
- }
- }
组合模式的扩展性非常好,没有各种条条框框,想怎么组合就怎么组合,比如所有书籍就是由各个分类组合而来的,你的leader马上又向你炫耀了统计所有书籍的浏览量和销售量的办法。
定义一个AllStatistics类实现了Statistics接口,具体代码如下所示:
- public class AllStatistics implements Statistics {
- private List<Statistics> statistics = new ArrayList<Statistics>();
- public AllStatistics() {
- statistics.add(new NovelStatistics());
- statistics.add(new TechnicalStatistics());
- }
- @Override
- public int getBrowseCount() {
- int browseCount = 0;
- for (Statistics s : statistics) {
- browseCount += s.getBrowseCount();
- }
- return browseCount;
- }
- @Override
- public int getSalesCount() {
- int saleCount = 0;
- for (Statistics s : statistics) {
- saleCount += s.getBrowseCount();
- }
- return saleCount;
- }
- }
当前组合结构的示意图如下:
现在你就可以非常方便的得到任何分类书籍的浏览量和销售量了,比如说获取科技类书籍的浏览量,你只需要调用:
- new TechnicalStatistics().getBrowseCount();
- new AllStatistics().getSalesCount();
当然你后面还可以对这个组合结构随意地改变,添加各种子分类书籍,而且子分类的层次结构可以任意深,正如前面所说,组合模式的扩展性非常好。
你的leader告诉你,目前他写的这份代码重复度比较高,其实还可以好好优化一下的,把冗余代码都去除掉。当然这个任务就交给你来做了,你的leader可是大忙人,早就一溜烟跑开了。
组合:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9400141
今天一大早,你的leader就匆匆忙忙跑过来找到你:“快,快,紧急任务!最近ChinaJoy马上就要开始了,老板要求提供一种直观的方式,可以查看到我们新上线的游戏中每个服的在线人数。”
你看了看日期,不是吧!这哪里是马上要开始了,分明是已经开始了!这怎么可能来得及呢?
“没关系的。”你的leader安慰你道:“功能其实很简单的,接口都已经提供好了,你只需要调用一下就行了。”
好吧,你勉为其难地接受了,对于这种突如其来的新需求,你早已习惯。
你的leader向你具体描述了一下需求,你们的游戏目前有三个服,一服已经开放一段时间了,二服和三服都是新开的服。设计的接口非常轻便,你只需要调用Utility.getOnlinePlayerCount(int),传入每个服对应的数值就可以获取到相应服在线玩家的数量了,如一服传入1,二服传入2,三服则传入3。如果你传入了一个不存在的服,则会返回-1。然后你只要将得到的数据拼装成XML就好,具体的显示功能由你的leader来完成。
好吧,听起来功能并不是很复杂,如果现在就开始动工好像还来得及,于是你马上敲起了代码。
首先定义一个用于统计在线人数的接口PlayerCount,代码如下:
- public interface PlayerCount {
- String getServerName();
- int getPlayerCount();
- }
- public class ServerOne implements PlayerCount {
- @Override
- public String getServerName() {
- return "一服";
- }
- @Override
- public int getPlayerCount() {
- return Utility.getOnlinePlayerCount(1);
- }
- }
- public class ServerTwo implements PlayerCount {
- @Override
- public String getServerName() {
- return "二服";
- }
- @Override
- public int getPlayerCount() {
- return Utility.getOnlinePlayerCount(2);
- }
- }
- public class ServerThree implements PlayerCount {
- @Override
- public String getServerName() {
- return "三服";
- }
- @Override
- public int getPlayerCount() {
- return Utility.getOnlinePlayerCount(3);
- }
- }
- public class XMLBuilder {
- public static String buildXML(PlayerCount player) {
- StringBuilder builder = new StringBuilder();
- builder.append("<root>");
- builder.append("<server>").append(player.getServerName()).append("</server>");
- builder.append("<player_count").append(player.getPlayerCount()).append("</player_count>");
- builder.append("</root>");
- return builder.toString();
- }
- }
- XMLBuilder.buildXML(new ServerOne());
- XMLBuilder.buildXML(new ServerTwo());
- XMLBuilder.buildXML(new ServerThree());
咦?你发现查看一服在线玩家数的时候,返回值永远是-1,查看二服和三服都很正常。
你只好把你的leader叫了过来:“我感觉我写的代码没有问题,但是查询一服在线玩家数总是返回-1,为什么会这样呢?”
“哎呀!”你的leader猛然想起,“这是我的问题,前面没跟你解释清楚。由于我们的一服已经开放一段时间了,查询在线玩家数量的功能早就有了,使用的是ServerFirst这个类。当时写Utility.getOnlinePlayerCount()这个方法主要是为了针对新开的二服和三服,就没把一服的查询功能再重复做一遍。”
听到你的leader这么说,你顿时松了一口气:“那你修改一下Utility.getOnlinePlayerCount()就好了,应该没我什么事了吧?”
“晤。。。本来应该是这样的。。。可是,Utility和ServerFirst这两个类都已经被打到Jar包里了,没法修改啊。。。”你的leader有些为难。
“什么?这不是坑爹吗,难道要我把接口给改了?”你已经泪流满面了。
“这倒不用,这种情况下可以使用适配器模式,这个模式就是为了解决接口之间不兼容的问题而出现的。”
其实适配器模式的使用非常简单,核心思想就是只要能让两个互不兼容的接口能正常对接就行了。上面的代码中,XMLBuilder中使用PlayerCount这个接口来拼装XML,而ServerFirst并没有实现PlayerCount这个接口,这个时候就需要一个适配器类来为XMLBuilder和ServerFirst之间搭起一座桥梁,毫无疑问,ServerOne就将充当适配器类的角色。修改ServerOne的代码,如下所示:
- public class ServerOne implements PlayerCount {
- private ServerFirst mServerFirst;
- public ServerOne() {
- mServerFirst = new ServerFirst();
- }
- @Override
- public String getServerName() {
- return "一服";
- }
- @Override
- public int getPlayerCount() {
- return mServerFirst.getOnlinePlayerCount();
- }
- }
需要值得注意的一点是,适配器模式不并是那种会让架构变得更合理的模式,更多的时候它只是充当救火队员的角色,帮助解决由于前期架构设计不合理导致的接口不匹配的问题。更好的做法是在设计的时候就尽量把以后可能出现的情况多考虑一些,在这个问题上不要向你的leader学习。
适配器:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 五种Java设计模式透析
- 设计模式 透析
- Java设计模式透析之 ——观察者模式(Observer)
- Java设计模式透析之单例模式
- Java设计模式透析之——策略模式
- Java设计模式透析之——适配器模式
- Java设计模式透析之——单例模式
- Java设计模式透析之 ——装饰模式(Decorator )
- Java设计模式透析--策略模式(一)
- Java设计模式透析--装饰者模式(二)
- Java设计模式透析之 —— 单例(Singleton)
- Java设计模式透析之 —— 策略(Strategy)
- Java设计模式透析之 —— 策略(Strategy)
- Java设计模式透析之 —— 单例(Singleton)
- Java设计模式透析之 —— 组合(Composite)
- Java设计模式透析之 —— 适配器(Adapter)
- [原]Java设计模式透析之 —— 适配器(Adapter)
- Java设计模式透析之 —— 适配器(Adapter)
- ubuntu14.04创建桌面快捷方式
- 算法_已知五个>=0的自然数,随机输入5个数,编写算法判断是否能排列成有序的数字。0可以替换成任何数
- 一些认识
- 第14章 事务
- CPU GPR与CP0寄存器汇编宏函数-读写
- 五种Java设计模式透析
- ArcGIS Server GP服务使用常见错误总结
- 在VS2012中实现Ext JS的智能提示
- 小m的区间公约数
- javascript之cookie对象
- IE6、IE7下绝对定位position:absolute和margin的冲突bug解决方法
- hdu1086
- java程序中如何动态设定cmd的初始盘符
- C实现最大公约数和最小公倍数