- 浏览: 409210 次
- 性别:
- 来自: 秦皇岛
最新评论
-
prayjourney:
了解了,讲的不错
DataInputStream和DataOutputStream类 -
niunianss:
将字节退回的时候,需要添加判断,最后一个字符是英文时,实际数组 ...
PushbackInputStream -
cctt_1:
不要误人子弟,那根本就不是 解释器模式!!!那是Composi ...
Interpreter(解释器)模式 -
java-大神:
[i][i]引用引用引用引用[/img][/img][/img ...
BufferedReader和BufferedWriter -
百合不是茶:
你的程序在文件输入输出流中传入agrs[0]时,会报错越界 ...
DataInputStream和DataOutputStream类
当构造某对象时,也许无法保证总能事先获知对象的所有所需信息。尤其是,有时候目标对象的构造器的参数只能逐步获取,我们希望能够一步一步地构造目标对象,常见情况比如解析器和用户接口。或者,当对类的重点难以很好把握并且类的构造过程相当复杂时,你也许希望简化类的规模,这时就可以使用Builder模式。
Builder模式的意图是把构造对象实例的代码逻辑移到要实例化的类的外部。
1. 常见的生成器:
使用Builder模式而获益的常见情况是定义所期望对象的数据被嵌套在文本字符串中。随着逐步查询代码或者说解析数据的过程中,你需要随着发现过程来保存这些数据。不管解析器是基于XML的还是手工执行的,最初也许不足以拥有构造合法目标对象所需要的全部数据。对这种情况,Builder模式的处理方式是把数据存储在临时对象中,直到程序拥有构造所需要的全部数据,这时候才查询存储的临时对象来构造目标对象。
假设除了生产焰火制品之外,Oozinoz公司偶尔还对外提供焰火表演服务。旅行社可以按照下面的模板向Oozinoz公司发送电子邮件申请预定焰火表演:
Date,November 5,Headcount,250,City,SprintField,
DollarPerHead,9.95,HasSite,False
可能你会猜到该协议一定是在XML之前诞生的。但是无论如何,该协议经实践证明的确是实用的。该预定请求说明预定焰火表演的时间和城市,以及最少来宾人数和身强体壮来宾的服务费用。通过上面这封邮件,该旅行社告诉Oozinoz公司,将有250名来宾观看这次的焰火表演,并且该旅行社愿意为每位来宾支持9。95美元,共计2487.50美元。另外,邮件还说明该旅行社还没有确定焰火表演的地点。
我们当前的任务就是解析这个文本性请求并创建一个Reservation对象来表示这次预定申请。为此,我们可以先创建一个属性为空的Reservation对象,并在解析器解析该文本请求的过程中逐步设置Reservation对象的各种属性。然而,这种做法存在这样一个问题:由此创建的Reservation对象并不一定能代表一次有效的焰火预定申请。例如,当我们的解析器解析完文本请求之后,可能会发现该文本请求没有说明表演日期。
我们可以创建一个ReservationBuilder类,以保证所创建的Reservation对象总能代表一次有效的焰火预定申请。该ReservationBuilder对象可以保存解析器解析出的各个预定请求属性,然后再利用这些属性参数创建Reservation对象,随后验证该对象的有效性。下图给出了该设计所涉及的类。
生成器类将某个领域的类的构造逻辑提取出来。每当解析器解析出一个初始化参数,
就把该参数交给生成器类对象
ReservationBuilder类的build()方法是抽象方法,因而ReservationBuilder类是个抽象类。根据基于不完整数据创建Reservation对象的方式不同,我们将构造非抽象的ReservationBuilder子类。ReservationParser类构造器把生成器作为参数,并向之传递信息。parse()方法从预定字符串读取信息,并传递给生成器,代码如下所示:
public void parse(String s) throws ParseException { String[] tokens = s.split(","); for(int i=0;i<tokens.length;i+=2) { String type = tokens[i]; String val = tokens[i+1]; if("date".compareToIgnoreCase(type) == 0) { Calendar now = Calendar.getInstance(); DateFormat formatter = DataFormat.getDateInstance(); Date d = formatter.parse( val + "," + now.get(Calendar.YEAR)); builder.setDate(ReservationBuilder.futurize(d)); }else if("headcount".compareToIgnoreCase(type) == 0) builder.setHeadcount(Integer.parseInt(val)); else if("City".compareToIgnoreCase(type) == 0) builder.setCity(val.trim()); else if("DollarPerHead".compareToIgnoreCase(type)==0) builder.setDollarsPerHead(new Dollars(Double.parseDouble(val))); else if("HasSite".compareToIgnoreCase(type)==0) builder.setHasSite(val.equalsIgnoreCase("true")); } }
当发现"date"时,解析器就解析接下来的值,并把日期保存起来。futurize()方法把年份放在前面,这样可以保证日期"November 5"解析为11月5日。当读者查看代码时,也许会发现解析器可能会误入歧途,从预定字符串的最初标志位开始解析。
突破题:split(s)调用使用的正则表达式对象会把逗号分隔的列表分隔为多个独立的字符串。请考虑如何改进这种正则表达式或整个方法,保证解析器能够更好地识别预定信息。
答:让解析器更加灵活的一种方式是允许接收逗号后的多个空格。为实现这一点,split()调用模式如下:
s.split(",*");
或者通过像如下代码那样初始化Regex对象,可以得到任何类型的空格。
s.split(",\\s*");
\s字符表示正则表达式中的空格“字符类”。请注意,所有解决方案都假设这些字段内没有嵌套逗号。
为了让这个正则表达式更加灵活,你也许会怀疑整个方法。尤其需要注意的是,你也许希望旅行代理能以XML格式来发送预定信息。你也许要建立一套标记,以便于XML解析器使用。
2.根据约束构建对象:
在这个例子中,我们必须保证绝不会实例化一个无效的Reservation对象。具体而言,假定每个有效的焰火表演预定申请必须明确指出表演日期和城市。另外,假定Oozinoz公司的商业规则规定每次焰火表演的观看人数必须大于或等于25人,或者总费用不得少于495.95美元。我们也许希望在数据库中记录这些限制,但现在我们使用Java代码把这些信息记录为常量,代码如下所示:
public abstract class ReservationBuilder { public static final int MINHEAD = 25; public static final Dollars MINTOTAL = new Dollars(495.95); //... }
观看人数太少或者收入太少的预定申请也会被视为无效的。为了避免在预定申请无效的情况下构造Reservation实例,我们可以在Reservation的构造器或者其调用的init()方法中加入进行商业规则检查的代码以及抛出异常的代码。但是,一旦创建了Reservation对象之后,这些商业规则就不会再被使用,它与Reservation对象的其他方法没有任何瓜葛。这个时候,我们可以创建一个生成器,并把Reservation的构造逻辑移入该生成器中。这样,Reservation类仅包含除构造之外的其他方法,从而变得更加简洁。另外,通过使用该生成器,我们还可以对Reservation对象的不同参数进行验证,并对无效参数做出相应处理。最后,通过将构造逻辑移入ResevationBuilder子类中,可以根据解析器解析出的参数逐步构造Reservation对象。下图给出了由ReservationBuilder类派生出的两个非抽象子类,这两个子类对无效参数的处理方式不同。
在根据给定的一组参数创建有效的对象的时候,生成器可能会遇到无效参数,这个
时候,有的生成器会抛出异常;有的生成器则会忽略
此图表更加清楚地说明了使用builder模式的好处:通过把构造逻辑和Reservation类本身分离开,我们可以把构造过程作为一个独立的任务来实现,甚至可以创建独立的生成方法层次关系。生成器行为中的差异也许对预定逻辑影响甚微。比如,上图的不同生成器区别在于是否抛出BuilderException异常。使用生成器的代码看起来如下代码所示:
package app.builder; import com.oozinoz.reservation.*; public class ShowUnforgiving { public static void main(String[] args) { String sample = "Date,November 5,Headcount,250," +"City,Springfield,DollarsPerHead,9.95," +"HasSite,False"; ReservationBuilder builder = new UnforgivingBuilder(); try { new ReservationParser(builder).parse(sample); Reservation res = builder.build(); System.out.println("Unforgiving builder:"+res); } catch (Exception e) { System.out.println(e.getMessage()); } } }
运行上述代码会输出一个Reservation对象:
Date,November 5,Headcount,250,City,SprintField,
Dollar/Head,9.95,Has Site,false
上面这个应用程序首先给定了一个预定请求字符串,接着实例化了一个生成器和一个解析器,随后开始利用解析器解析该字符串。读入该预定的请求字符串之后,该解析器便开始不断地将解析出来的预定申请信息通过生成器的set方法传给生成器。
预定请求字符串解析完毕之后,应用程序便用该生成器构造一个有效的预定对象。当出现异常的时候,该应用程序仅打印出该异常有关的文字信息。而在实际的应用中,当出现异常的时候,我们需要完成一些重要的异常处理工作。
突破题:当预定申请信息中的日期或者城市属性为空,或者观看人数太少,或者焰火表演的整场演出费用太低时,UnforgivingBuilder类的build()方法就会抛出BuilderException异常。请据此说明写出build()方法的实现。
答:若所有属性都是有效的,则build()方法就会返回有效的Reservation对象;否则,该方法就会抛出异常。下面是该方法的一种实现:
public Reservation build() throws BuilderException { if(date == null) throw new BuilderException("Valid date not found"); if(city == null) throws new BuilderException("Valid city not found"); if(headCount < MINHEAD) throws new BuilderException("Minimum headcount is:"+ MINHEAD); if(dollarsPerHead.times(headcount).isLessThen(MINTOTAL)) throws new BuilderException("Mininum total cost is" + MINTOTAL); return new Reservation( date, headCount, city, dollarsPerHead, hasSite); }
ReservationBuilder超类定义常量MINHEAD和MINTOTAL。
如果这个生成器没有遇到问题,则会返回一个有效的Reservation对象。
3.根据不完整信息构造符合约束的对象:
UnforgivingBulder类将拒绝任何信息不完整的请求。公司可能会期望在客户的预定申请缺少某些信息的情况下,我们的软件系统能够对该系统进行适合的修改。
具体而言,假定申请中没有指明观看焰火的人数,分析人员会要求我们根据公司的商业规则为观看人数设置一个最小值。同样,如果预定申请中没有指明焰火表演的单人费用,我们可以为之设置一个合适的费用,从而使得整场演出费达到该商业规则指定的最小值。这些需求相当简单,但是设计起来需要一些技巧。比如,如果预定字符串提供单人费用数据值,但是没有提供总人数,生成器应该怎么办?
突破题:请写出ForgivingBuilder.build()方法的规范,说明当预定字符串没有提供总人数或者单人费用时,生成器应该怎么办?
答:像以前一样,如果预定活动没有指定城市或者日期,则程序会抛出异常,因为无法预测这些数据。如果缺少总人数或者人均费用,请注意以下几点:
(1)如果预定请求没有说明总人数和人均费用,则程序会自动把总人数设置为最小值,把人均费用设置为最小总费用除总人数。
(2)如果预定请求没有说明总人数,但是指定了人均费用值,则程序会把总人数自动设置为最小值,保证总费用足够维持本次活动。
(3)如果预定请求指定了总人数,但是没有指定人均费用,则程序会把人均费用设置为某个值,保证总费用足够维护本次活动。
突破题:请写出ForgivingBuilder类中build()方法的实现。
public Reservation build() throws BuilderException { boolean noHeadCount = (headCount == 0); boolean noDollarsPerHead = (dollarsPerHead.isZero()); if(noHeadcount && noDollarsPerHead) { headCount = MINHEAD; dollarsPerHead = sufficientDollars(headCount); }else if(noHeadCount) { headCount = (int)Math.ceil(MINTOTAL.divideBy(dollarsPerHead)); headCount = Math.max(headcount,MINHEAD); }else if(noDollarsPerHead) { dollarsPerHead = sufficientDollars(headCount); } check(); return new Reservation( date, headCount, city, dollarsPerHead, hasSite); }
上述代码依赖于check()方法,这个方法类似于UnforgivingBuilder类的build()方法。
protected void check() throws BuilderException { if(date == null) throw new BuilderException("Valid date not found"); if(city == null) throw new BuilderException("Valid city not found"); if(headcount<MINHEAD) throw new BuilderException("Minimum headcount is "+MINHEAD); if(dollarsPerHead.times(headcount).isLessThan(MINTOTAL)) throw new BuilderException("Minimum total cost is "+MINTOTAL); }
ForgivingBuilder类和UnforginvBuilder类可以确保Reservation对象始终有效。当构造预定对象出现问题时,你的设计也应该提供足够的灵活性来解决出现的问题。
4.小结:
Builder模式将一个复杂对象的构造逻辑从其代码中分离出来。其直接的效果就是简化了原来复杂的目标对象。生成器类集中负责目标类对象的构造,而目标类则集中完成有效实例的各种非构造操作。其中模式的一个突出优点体现在,我们在实例化目标类之前可以能够构造一个有效的对象,而且不必将这些构造逻辑放在目标类的构造器中。另外,Builder模式还使得我们可以逐步构造目标类对象。这个特点使得Builder模式特别知县于通过解析文本获取对象信息,或者从图形用户界面收集对象信息来创建对象的场合。
发表评论
-
工厂方法模式示例
2009-11-27 14:34 1407//工厂方法模式示例: package innerclass ... -
Flyweight(享元)模式
2009-05-15 21:24 1182Flyweight模式可实现客户 ... -
责任链(Chain of Responsibility)模式
2009-05-15 21:18 1826面向对象开发 ... -
Prototype(原型)模式
2009-05-15 21:17 1489Prototype模式不通过实例化类来创建一个新的未初始化的实 ... -
Proxy(代理)模式
2009-05-15 21:14 1528Proxy(代理)模式 ... -
Mediator(中介者)模式
2009-05-15 21:14 1478通常,面向对象的软 ... -
Observer(观察者)模式一
2009-05-15 21:13 1561Observer模式的宗旨是在多个对象之间定义一对多的关系,以 ... -
责任型模式
2009-05-15 21:12 1013对象的责任类似于 ... -
Bridge(桥接模式)
2009-05-15 21:11 1420Bridge模式着重考虑如何实现对具体实现的抽象(ab ... -
Facade外观模式
2009-05-15 21:11 29041.Facade模式就是一个功能介于包和完整应用程序之 ... -
Composite(组合)模式
2009-05-15 21:07 1774Composite模式涉及的是一组对象,其中有些 ... -
Memento(备忘录)模式
2009-05-15 21:07 2203有时候,我们需要创建的对象曾经在系统中出现过。这种情形可能出现 ... -
操作型模式简介
2009-05-15 21:01 1158我们只有弄清楚诸如算法、多态性、方法和操作等术语的不同意 ... -
Template Method(模板方法)模式
2009-05-15 21:01 1081普通方法的方法体 ... -
Template Method二
2009-05-15 20:59 1233Template Method模式类似于Adapter模 ... -
State(状态)模式
2009-05-15 20:58 1441对象的状态是其属性当 ... -
Strategy(策略)模式
2009-05-15 20:55 1076算法是定义好的过程, ... -
Strategy(策略)模式二
2009-05-15 20:54 13592.重构为Strategy模式: ... -
Command(命令)模式
2009-05-15 20:52 1544让方法运行的最常见 ... -
Command模式二
2009-05-15 20:50 2150假设现有一个新需求要求我们统计方 ...
相关推荐
C#设计模式之Builder生成器模式解决带老婆配置电脑问题实例 本文主要介绍了C#设计模式之Builder生成器模式解决带老婆配置电脑问题,简单介绍了生成器模式的概念、功能并结合具体实例形式分析了C#生成器模式解决配...
在这里与各位分享本人从网络上下载的C#面向对象设计模式纵横谈系列视频,共有25节,除了第一节需要各位贡献一点资源分以作为对本人上传资源的回馈,后面的其他资源均不需要... 这是第4节:创建型模式Builder生成器模式
Builder模式是一种创建型设计模式,它提供了一种...在观看“C#视频-面向对象设计模式纵横谈(4):Builder 生成器模式(创建型模式)”的视频教程时,可以深入理解Builder模式的工作原理,学习如何在实际项目中有效应用。
在C#中,Builder模式常常应用于游戏对象生成、配置文件解析、数据库记录映射等领域。例如,游戏中的角色创建,可能需要设置角色的属性、技能等,这些可以通过不同的Builder来实现;而在数据库操作中,ORM框架如...
Builder模式是一种创建型设计模式,它提供了一种创建对象的抽象接口,并允许使用不同的实现来创建复杂的对象。在C#中,Builder模式可以帮助我们在不暴露复杂构造过程的情况下,创建具有多种构建步骤的对象。这种模式...
生成器模式(Builder Pattern)是一种设计模式,它允许我们分步骤构建复杂对象,而无需暴露其构造过程。这种模式在创建对象时提供了更大的灵活性,特别是当构造过程需要多个步骤或者对象有不同的构造方式时。Builder...
生成器模式是一种设计模式,属于创建型模式,它允许我们分步骤构建复杂对象,而无需提前知道整个对象的完整结构。这种模式的核心在于延迟初始化,它使得我们可以根据需要逐步构建对象,而不是一次性创建所有部分。在...
生成器模式(Builder Pattern)是一种创造型设计模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建可以创建不同的表示。这种模式可以在以下情况下使用: 1. 当创建复杂对象的算法应该独立于该对象的组成...
生成器模式通常由四个主要角色组成:Director(导演)、Concrete Builder(具体生成器)、Product(产品)和Builder(生成器接口)。 在C#中,我们可以定义一个生成器接口,包含创建产品各个部分的方法,如`...
生成器模式(Builder Pattern)是Java设计模式中的创建型模式之一,主要解决复杂对象的构建问题,通过将构造过程逐步分解,使得构造过程与表示分离,使得同样的构建过程可以创建不同的表示。这种模式通常用于创建...
Builder模式是一种设计模式,主要目的是将复杂对象的构建与其表示分离,使得构建过程可以独立于表示进行。在Builder模式中,我们通常会定义一个抽象Builder类,它规定了如何创建复杂对象的各个部分,然后创建具体...
生成器模式,也被称为构建器模式,是一种软件设计模式,主要用在对象的创建过程中,它将复杂的构建过程分解为一系列简单的步骤,使得构建过程可配置,并且可以独立于对象的表示进行。在C#中,生成器模式的实现通常...
在iOS开发中,生成器模式(Builder Pattern)是一种设计模式,它允许我们分步骤构建复杂的对象,而无需暴露创建过程的复杂性。这种模式在处理需要多种构建方式或需要逐步构造对象的情况时特别有用。标题“iOS 生成器...
生成器模式是一种设计模式,属于创建型模式,它在软件工程中被广泛应用于解决大量对象的创建问题。这种模式的主要思想是将对象的创建过程分解为一系列步骤,使得客户端可以根据需要选择执行这些步骤,从而实现延迟...
Builder 模式(生成器模式) Builder 模式是一种创建型设计模式,它将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。GoF 将其定义为将一个复杂对象的构建与它的表示分离,使得同样的...
生成器模式,也称为建造者模式,是一种设计模式,用于创建复杂对象,它允许你按照一系列步骤来构造对象,而这些步骤可以通过不同的实现来产生不同形式的对象。生成器模式的核心在于分离了对象的构造过程和表示细节,...
Builder 模式(生成器模式) Builder 模式是一种创建型设计模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Builder 模式与 Abstract Factory 模式非常像,但是它们之间有着...
在《Head First Design Patterns》文中是这么介绍生成器模式的,“封装一个产品的构造过程,并允许按步骤构造”,感觉说得不是很清楚。而在网上查找相关资料是这么解释的,建造者模式(Builder Pattern)使用多个简单...
C#面向对象设计模式纵横谈(4):Builder 生成器模式(创建型模式) C#面向对象设计模式纵横谈(5):Factory Method 工厂方法模式(创建型模式) C#面向对象设计模式纵横谈(6):Prototype 原型模式(创建型模式) C#面向...