Chapter58设计模式简介2013_第1页
Chapter58设计模式简介2013_第2页
Chapter58设计模式简介2013_第3页
Chapter58设计模式简介2013_第4页
Chapter58设计模式简介2013_第5页
已阅读5页,还剩75页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1SoftwareEngineering

DesignPatternsSoftwareEngineering

FoundationsofModelDesign

FifthEditionby

Shiqinghua&John2软件设计模式

那些事儿设计模式并不是一种独立的方法和技术,而是一种思想和方法论,主要是面向对象技术及思想的拓展。设计模式和具体的语言没有关系,学习设计模式最重要的是建立面向对象的基本思想,尽可能的在设计中实现各种概念:面向接口编程、低耦合、高内聚、可复用、可扩展等等。设计模式是前人经验的积累,目的是让软件设计变得更像一个艺术品,而不是一堆难以维护和重用的代码集合。3模式(Pattern):是在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案。设计模式(DesignPattern):是一套被反复使用的(多数人知晓并经过分类编目的)代码设计经验的总结,使用设计模式目的是为了可重用代码、让代码更容易被他人理解并且保证软件质量。说明:设计模式是一种针对单个软件模块或少量模块而给出的一般性解决方案,它提供较低层次的设计决策。是软件体系结构之下的应对各种软件需求的具体设计思考模式。4Section01

FactoryPattern

工厂模式SoftwareEngineering

FoundationsofModelDesign

FirthEditionby

Shiqinghua&John5模式设计之简单工厂某初学者进入某软件公司做程序员,经过短期java培训,进入某项目组。开发任务:某企业有很多异地的子公司,需要设计一个“薪资计算程序”。设计思路:以面向对象思想为基础,从基本的程序设计(首先是个能解决薪资计算的基本程序)出发,随着需求的变更,伴随各种问题的出现、分析及询问,不断的优化程序设计方法。6模式设计之简单工厂初学者的设计思路:首先设计一个抽象薪资类,然后所有的子公司都继承这个抽象的薪资类。抽象薪资类代码如下:/*GeneratedbyTogether*/packagecom.gongdan.sampleFactory;publicinterfaceSalary{voidcomputerSalary();}7模式设计之简单工厂河北和吉林两个子公司的薪资类:packagecom.gongdan.sampleFactory;//河北子公司publicclassHeBeiSalaryimplementsSalary{publicvoidcomputerSalary(){//执行河北自己的计算规则System.out.println("开始计算河北子公司的薪资");}}packagecom.gongdan.sampleFactory;//吉林子公司publicclassJiLinSalaryimplementsSalary{publicvoidcomputerSalary(){//执行吉林自己的计算规则System.out.println(“开始计算吉林子公司的薪资");}}8客户端的示意代码如下:(客户可能使用并维护)packagecom.gongdan.sampleFactory;publicclassClient{

/*根据参数创建不同的对象*///第二个方法(随后执行)publicSalarycreateSalary(Stringname){Salarysalary=null; if("HeBei".equals(name))salary=newHeBeiSalary();elseif("JiLin".equals(name)) salary=newJiLinSalary();returnsalary;}/*具体的薪资计算*///第一个方法(首先执行)publicvoidcomputerSalary(Stringname){//用户输入省份名Salarysalary=createSalary(name);Salary.computerSalary();}}9薪资项目的程序分析客户端程序的分析:优点:薪资计算程序比较清晰潜在问题:若总公司一旦兼并很多公司的话,则客户端的条件判断要写很多,代码肯定会越来越长。当然,后台的其他设计(其他一些类的数量)增长也是应该且合理的,他们可以不放在客户端。10薪资项目的程序改进为了解决兼并多家公司的问题,于是在同事的帮助下,该初学者对上述薪资程序的的改进思路如下:1.设计抽象薪资类,如前一样。2.设计工厂类,把具体产生薪资类的判断语言抽取到工厂类里面,且使用java的反射机制;而客户端则只负责具体的薪资计算------形成“简单工厂”的设计思路11薪资项目的简单工厂模式简单工厂类代码如下(可以不在客户端,即在后台维护)packagecom.gongdan.sampleFactory;publicclassSampleFactory2{

/*根据参数创建不同的对象*/publicstaticSalarycreateSalary(Stringname){ if("HeBei".equals(name))returnnewHeBeiSalary();elseif("JiLin".equals(name)) returnnewJiLinSalary();returnnull;}}12薪资项目的程序改进客户端的代码如下:packagecom.gongdan.sampleFactory;publicclassClient1{publicstaticvoidmain(String[]args){Salarysalary=SampleFactory2.createSalary("HeBei");puterSalary();salary=SampleFactory2.createSalary("JiLin");puterSalary();}}13薪资项目的简单工厂模式分析简单工厂设计模式的总结:优点:客户端不再负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责针对对象的方法调用,从而明确了各个类的职责。(工厂类的维护可以不在客户端进行)缺点:简单工厂模式使用静态方法来创建对象,这就导致静态方法无法继承。这样的系统扩展性不好。(工厂太简单)工厂类虽然负责对象的创建,但随着同类产品的增多,且客户端可能对于产品创建方式(初始化时,有的创建时需要判断不同日期,有的需要判断其他任务完成情况等等)会有不同要求,这样的话,就需要不断修改工厂类,增加相应的判断逻辑,还是不利于软件后期维护。总结:简单工厂模式适用于要创建的具体对象比较少或简单的情况,即:此时工厂简单了点!而创建比较复杂的对象时,大的工厂类就应该变成一个个小的“配套”工厂类--------“工厂模式”。(换言:简单工厂类是一个系统扩展设计的雏形)简单来料加工14进化:薪资项目的工厂模式程序抽象的薪资类和每个公司的薪资类不变把工厂类抽象化:----工厂抽象类代码如下:packagecom.gongdan.FactoryMethod;publicinterfaceFactory1{publicSalarycreateSalary();}15薪资项目的工厂模式程序河北和吉林两个公司的工厂类代码:packagecom.gongdan.FactoryMethod;publicclassHeBeiSalaryFactoryimplementsFactory1{publicSalarycreateSalary(){ returnnewHeBeiSalary();//⑴这里可以有复杂的创建逻辑}}//多一句:其computersalary()可细化?packagecom.gongdan.FactoryMethod;publicclassJiLinSalaryFactoryimplementsFactory1{publicSalarycreateSalary(){ returnnewJiLinSalary();//⑵具体的创建由配套工厂创建。}}16薪资项目的工厂模式程序工厂模式的客户端代码:packagecom.gongdan.sampleFactory;publicclassClient{publicstaticvoidmain(String[]args){Factoryfactory=newHeBeiSalaryFactory();Salarysalary=factory.createSalary();//创建河北薪资类puterSalary();//计算河北子公司的薪资 salary=newJiLinSalaryFactory();salary=factory.createSalary();puterSalary();//计算吉林子公司的薪资}}17薪资项目的程序分析客户端的示意代码如下:这里指:salary18薪资项目的工厂模式客户端的示意代码如下:19薪资项目的工厂设计模式分析工厂设计模式的总结:优点:客户端不再负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责针对对象的方法调用,从而明确了各个类的职责。即:让客户端只做扩展!不做修改!有新产品/软件任务加入时,只需要新增加一个具体的创建产品工厂类(HeBeiSalaryFactory)和具体的产品类(HeBeiSalary)(这里是河北子公司(的薪资))就可以了,不会影响到原来已有的其他代码,代码量也不会无序变大,后期维护容易,增强了系统的可扩展性。解决了各个创建产品初始化凌乱的问题缺点:该工厂模式还是需要额外的编写代码,增加了工作量。总结:工厂模式适用于要创建“一个”产品系列。创建同类产品需要一个工厂,而创建不同类产品就需要不同的工厂,而创建不同大类产品更有效的方法是--------“抽象工厂模式”20薪资项目的工厂设计模式分析总结:工厂模式适用于要创建“一个”产品系列。但是:例如:创建一个摩托车上的螺丝和螺母,则可以需要一个用来创建螺丝的工厂类和一个用来创建螺母的工厂类,分别来创建两个用于摩托车的螺丝和螺母,然后再装配。如果此时用户又需要创建一个用于汽车上的螺丝和螺母,则还需要再编写一个用于汽车螺丝的工厂类和螺母的工厂类,这样就需要四个工厂类,。。。。实际情况:螺丝和螺母一般不会孤立地进行生产,可以考虑直接用一个工厂类来同时生成一个用于摩托车的螺丝和螺母,再用另一个工厂类来同时生成一个用于汽车的螺丝和螺母,这样只用两个工厂类即可实现前面四个工厂类的工作,这就是-----------“抽象工厂模式”。即:解决系列相关多个产品的生产!21薪资项目的抽象工厂模式程序背景具体到关于薪资计算的扩展性新需求:

薪资计算往往不是这么简单,假如计算完薪资后,一般还要同时也计算或处理社会保险,个人所得税等,如何处理?若采用工厂模式,则需要再定义两个抽象工厂类,分别用来定义社会保险、个人所得税的创建方式。然后每个子公司再分别继承这两个抽象工厂类,以实现具体的工厂类。这样,每个子公司至少需要三个工厂类:薪资、社保、工人所得税。如果子公司有多个,那工厂类的数量可想而知。22薪资项目的抽象工厂模式程序解决方法:首先,社会保险、个人所得税和薪资类的设计一样。从抽象接口设计开始。抽象的社保类的定义如下:/*Insurance.java*/packagecom.gongdan.AbstractFactory;publicinterfaceInsurance{voidcomputerInsurance();}23薪资项目的抽象工厂模式程序河北和吉林子公司的社保类定义如下:packagecom.gongdan.AbstractFactory;publicclassHeBeiInsuranceimplementsInsurance{publicvoidcomputerInsurance(){System.out.println("开始计算河北子公司的社会保险");}}packagecom.gongdan.AbstractFactory;publicclassJiLinInsuranceimplementsInsurance{publicvoidcomputerInsurance(){System.out.println("开始计算吉林子公司的社会保险");}}24薪资项目的抽象工厂模式程序抽象的个人所得税类的定义如下:/*Tax.java*/packagecom.gongdan.AbstractFactory;publicinterfaceTax{voidcomputerTax();}25薪资项目的抽象工厂模式程序河北和吉林子公司的所得税类定义如下:packagecom.gongdan.AbstractFactory;publicclassHeBeiTaximplementsTax{publicvoidcomputerTax(){System.out.println("开始计算河北子公司的所得税");}}packagecom.gongdan.AbstractFactory;publicclassJiLinTaximplementsTax{publicvoidcomputerTax(){System.out.println("开始计算吉林子公司的所得税");}}26薪资项目的抽象工厂模式程序再定义一个抽象的工厂类,用来定义创建薪资类、社保类和所得税类的方式(在一个大类里面有多个相关产品)/*Factory.java*/packagecom.gongdan.AbstractFactory;publicinterfaceFactory1{SalarycreateSalary();InsurancecreateInsurance();TaxcreateTax();}27薪资项目的抽象工厂模式程序河北子公司的工厂类(创建河北的多个相关产品)packagecom.gongdan.AbstractFactory;publicclassHeBeiFactoryimplementsFactory1{/*创建河北薪资对象*/publicSalarycreateSalary(){ returnnewHeBeiSalary();//这里可以有复杂的创建逻辑} /*创建河北社保对象*/publicInsurancecreateInsurance(){ returnnewHeBeiInsurance();} /*创建河北所得税对象*/publicTaxcreateTax(){ returnnewHeBeiTax();}}28薪资项目的抽象工厂模式程序吉林子公司的工厂类:packagecom.gongdan.AbstractFactory;publicclassJiLinFactoryimplementsFactory1{/*创建吉林薪资对象*/publicSalarycreateSalary(){ returnnewJiLinSalary();//这里可以有复杂的创建逻辑} /*创建吉林社保对象*/publicInsurancecreateInsurance(){ returnnewJiLinInsurance();} /*创建吉林所得税对象*/publicTaxcreateTax(){ returnnewJiLinTax();}}29薪资项目的抽象工厂模式程序客户端的示意代码如下(只是添加/扩展,不是直接的修改)packagecom.gongdan.AbstractFactory;publicclassClient{publicstaticvoidmain(String[]args){

Factory1factory=newHeBeiFactory(); Salarysalary=factory.createSalary(); puterSalary(); Insuranceinsurance=factory.createInsurance(); puterInsurance();Taxtax=factory.createTax(); puterTax();factory=newJiLinFactory(); salary=factory.createSalary(); puterSalary(); insurance=factory.createInsurance(); puterInsurance();tax=factory.createTax(); puterTax();}}30薪资项目的抽象工厂模式程序客户端的示意代码如下:31薪资项目的抽象工厂模式程序客户端的示意代码如下:32薪资项目的抽象工厂模式分析抽象工厂设计模式的总结:优点:客户端不再负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责针对对象的方法调用,从而明确了各个类的职责。当一系列相互关联的产品被设计到一个工厂类后,客户端的调用将变得非常简单,而且,如果要更换这一系列的产品,只需要更换一个工厂类即可。缺点:如果有新的相关系列产品加进来,则需要修改抽象工厂类的设计,并同时修改实现这个抽象工厂类的具体工厂类,需要额外的编写代码,增加了工作量。总结:抽象工厂设计模式的实质:增加多个系列的相关产品时,不能靠简单的增加工厂类来解决问题,而是设法在已有的工厂类中依靠增加(抽象)方法来实现。提示:当创建另外大类的产品时,只好增加抽象工厂了!33Section02

BuilderPattern

创建者模式SoftwareEngineering

FoundationsofModelDesign

FirthEditionby

Shiqinghua&John34薪资项目的创建者模式关于薪资计算的抽象工厂模式带来的问题:薪资计算往往不是这么简单,假如计算完薪资、社会保险,个人所得税之后,还要对这些结果进行整理,有时还要组装成为新的对象(例如报表),而按照抽象工厂模式的话,就不得不在客户端进行!这将带来很大的工作量,如何处理?(软件零件/工件有时要进行某种组装!)一种解决办法:可以把零件的组装放在工厂类里面(添加一个“组装”方法),但这违反了工厂类单一职责的设计原则,使得该类既要负责对象的创建,又要负责产品的组装。正确的解决方案:针对比较复杂的产品,设计一个单独的类来负责产品的组装。(组装类)35薪资项目的创建者模式增加的薪资项目的组装类示意代码如下:packagecom.gongdan.Builder;publicclassDirector{//组装类的定义privateFactoryfactory;publicDirector(Factoryfactory){//构造方法 this.factory=factory;}publicvoidcomputer(){Salarysalary=factory.createSalary(); puterSalary(); Insuranceinsurance=factory.createInsurance(); puterInsurance();Taxtax=factory.createTax(); puterTax(); ……//以下的代码可以对产品进行整理组装改型等。}}36薪资项目的创建者模式薪资程序客户端的示意代码如下:packagecom.gongdan.Builder;publicclassClient{publicstaticvoidmain(String[]args){Directordirector=Director(newHeBeiFactory());puter();//具体产品的整理组装等不在客户端director=Director(newJiLinFactory());puter();}}来自学习者的问题:当需求变更时,是不是就得更换一个个“更大”的详细设计框架,把问题“套进去”?

回答:是的,设计模式在高级语言和汇编语言时代就存在,只不过那时候的代码规模很大而已。3738薪资项目的创建者模式总结:创建者模式的客户端不再负责对象的创建和组装,而是把这个创建的职责交给工厂类,把组装的职责交给组装类,客户端只负责对象的调用,从而明确了各个类的职责。适合产品差异不大,但组装很复杂的情况。缺点:如果产品的差异非常大,则只好要编写多个创建者类才能实现目标要求了。(因为创建者类也要遵循单一职责的原则。)39Section03

FacadePattern

外观模式SoftwareEngineering

FoundationsofModelDesign

FirthEditionby

Shiqinghua&John40一目了然:外观模式引言:在所有的设计模式中,外观模式是一种较简单的设计模式,顾名思义就是对一个系统进行包装,该系统对外的接口统一由外观类进行提供。从系统间的衔接谈起:该软件设计初学者最近负责公司“人力资源系统”的设计工作,而公司的内部网是由其他人设计的,内部网上的通信录需要人力资源系统提供人员的相关信息,而且要求可以根据组织机构获取某一组织机构下的人员信息等。设计思路:人力资源系统的程序里面虽有内部网所需要的接口,但都分布在不同的程序代码里,要是内部网真的直接使用了这些接口,一旦该初学者自己想修改程序,就不能再随便改动了,最好有一个统一的类来提供人力资源系统对外的接口。使用时机:当一个复杂的系统需要对外提供接口时,就需要将对外提供的接口统一封装在一个外观类里,供外部系统使用。41外观模式定义:外观模式就是为子系统对外提供的一组接口提供一个统一的界面,使得其他系统对该系统的访问都通过这个统一的界面来完成。原理:外观模式主要由三部分组成:抽象外观类、实现抽象外观类的具体外观类、其他子系统。publicclassOtherSystem{//其他子系统:类的示意代码publicstaticvoidmain(String[]argv){Facadefacade=newConcreteFacade();facade.operation1();facade.operation2();}/***@directed*/privateFacadelnkFacade;}42外观模式抽象外观类的示意代码:/*GeneratedbyTogether*/packagecom.gongdan.Facade;publicinterfaceFacade{voidoperation2();voidoperation1();}

43外观模式具体外观类的示意代码:packagecom.gongdan.Facade;publicclassConcreteFacadeimplementsFacade{publicvoidoperation1(){SubSystemsub=newSubSystem();//要编写的代码:如何使用获取来的接口数据,在这里由

//其他子系统的人自己书写,这里的界面可以是制定为统一

//风格的。

}publicvoidoperation2(){SubSystem1sub=newSubSystem1();//要编写的代码

}/**@linkdependency*//*#SubSystemlnkSubSystem;*//**@linkdependency*//*#SubSystem1lnkSubSystem1;*/}44外观模式子系统类的示意代码如下:/*GeneratedbyTogether*/packagecom.gongdan.Facade;publicclassSubSystem{//本子系统的数据的获取在这里编写,一旦修改了自己子系统

//的内部数据接口,则由该软件设计初学者自己修改此处。}/*GeneratedbyTogether*/packagecom.gongdan.Facade;publicclassSubSystem1{}45外观模式46外观模式总结:外观模式就是通过提供一个统一的对外接口界面,使得其他系统对该系统的访问都通过这个统一界面来完成,避免了外部系统和本子系统之间的直接联系,从而降低了系统之间的依赖和复杂度等等。缺点:限制了外部系统对本子系统调用的灵活性,只能按照外观类中提供的方式对本子系统进行访问。47用外观模式实现持久层框架的设计引言:外观模式就是通过提供一个统一的对外接口,这在早期的JDBC编程时用处很大,软件团队要求该初学者实现一个持久层框架,因此他就使用外观模式来封装java提供的JDBC技术,从而方便其他软件开发人员对JDBC的使用。基本逻辑:在进行JDBC编程时,熟练的开发者知道直接使用java提供的JDBC会很繁琐,因此大家都使用较成熟的开源持久层框架,比如Hibernate或自己编写持久层框架,也就是对java提供的JDBC进行封装,当以后使用JDBC时,都是使用同一个类里的方法,即使以后要修改底层实现,只需要修改封装后的持久层框架就可以了,这就是外观模式的应用。48用外观模式实现持久层框架的设计持久层框架的设计思路(以下大约总计500-600条语句)1.编写数据库连接的接口,在接口中定义获取连接和进行事物处理的方法。2.编写数据库连接的实现类。3.编写数据库处理结果集的接口,在接口中定义必须要实现的几个获取数据或更新数据的方法。4.编写数据库结果集的实现类。49外观模式的实际应用之二问题:现实中的茶馆系统中国茶文化源远流长,博大精深,茶已经成为国人生活中不可或缺的必需品。大家有没有比较过自己泡茶喝去茶馆喝茶的区别?自己泡茶需要自行准备茶叶、茶具和开水,而去茶馆喝茶,最简单的方式就是跟茶馆服务员说想要一壶什么样的茶(铁观音、碧螺春或者西湖龙井)。正因为有茶馆服务员,顾客无需直接和茶叶、茶具和开水等交互,整个泡茶过程由服务员来完成,或者自己按照个人习惯自己泡!顾客只需要和服务员简单交互即可,非常简单省事。软件系统有时需要类似服务员一样的角色。大的外观式接口可以写成类似中间件系统。外观模式通过引入一个新的外观类来实现该功能,它为多个业务类的调用提供了一个统一接口,简化了类与类间的交互。50外观模式的实际应用之二实际软件设计问题:文件加密模块的设计欲开发一个可应用于多个软件的的文件加密模块,该模块可以对文件中的数据进行加密并将加密之后的数据存储到一个新文件中。具体的流程包含三个部分:源文件读取、加密、保存加密之后的文件。其中读取文件和保存文件使用流来实现,加密操作通过求模运算来实现,这三个操作相对独立,为了实现代码的独立重用,让设计更符合单一职责原则,这三个操作的业务代码将封装在3个不同的类中。接下来,就牵扯到外观模式的具体设计工作。51Section04

SingletonPattern

单例模式SoftwareEngineering

FoundationsofModelDesign

FirthEditionby

Shiqinghua&John52独一无二:单例模式单例模式的动机:对于一个软件系统中的某些类而言,无须创建多个实例,而是仅需要创建一个独一无二的对象。这是一个很简单实用的设计模式。熟知的例子:windows任务管理器。每次能打开几个窗口?另注:计算机中毒或私自修改内核者除外。打开多个窗口之后带来的严重问题。若能弹出多个窗口且内容一致,则系统具有多个重复对象,浪费资源若能弹出多个窗口且内容不一致,则系统的资源使用情况和进程、服务等信息呈现多个状态,问题更严重!53单例模式概述模拟实现windows任务管理器:类名:TaskManager成员方法:构造方法,显示进程的方法,显示服务的方法。示意代码:publicclassTaskManager{publicTaskManager(){…….}publicvoiddisplayprocesses(){……}publicvoiddisplayservices(){……}…………..}54单例模式概述为了实现windows任务管理器的对象唯一性,需要完成以下三个步骤对类TaskManager进行重构:构造函数可见性私有化:privateTaskManager(){…….}避免内部重复创建:privatestaticTaskManagertm=null;为保证外部访问者使用,特增加公有的静态方法如下:(一个副本,外部可用。)

publicstaticTaskManagergetinstance(){if(tm==null){tm=newTaskManager();}returntm;}55单例模式概述完成最简单的单例类的设计:publicclassTaskManager{privatestaticTaskManagertm=null;privateTaskManager(){…….}//初始化窗口publicvoiddisplayprocesses(){……}//显示进程publicvoiddisplayservices(){……}//显示服务publicstaticTaskManagergetinstance(){if(tm==null){tm=newTaskManager();}returntm;}…………..}5657单例模式应用之二

----负载均衡器的设计软件设计任务:某公司承接了一个服务器负载均衡(LoadBalancer)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高系统的整体处理能力,缩短响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,即只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。关键点:确保负载均衡器实例的唯一性。58----负载均衡器的设计//将负载均衡器LoadBalancer设计为单例类(只列出核心代码)publicclassLoadBalancer{privatestaticLoadBalancerinstance=null;//私有静态变量privateListserverList=null;//服务器集合privateLoadBalancer(){//私有初始化/构造窗口serverList=newArrayList();。。。。。。

}//以下方法为公有静态成员,返回唯一实例。publicstaticLoadBalancergetLoadBalancer(){if(instance==null){instance=newLoadBalancer();}returninstance;}59//本页接上页内容publicvoidaddServer(Stringserver){//增加服务器serverList.add(server);}publicvoidremoveServer(Stringserver){//删除服务器serverList.removr(server);}//以下使用Random类随机获取服务器publicStringgetServer(){Randomrandom=newRandom();inti=random.nextInt(serverList.size());return(String)serverList.get(i);}}60编写如下客户端测试代码(注:在此仅为测试之用)publicclassClient{publicstaticvoidmain(String[]args){//以下创建4个LoadBalancer对象LoadBalancerbalancer1,balancer2,balancer3,balancer4;balancer1=LoadBalancer.getLoadBalancer();balancer2=LoadBalancer.getLoadBalancer();balancer3=LoadBalancer.getLoadBalancer();balancer4=LoadBalancer.getLoadBalancer();//以下判断服务器负载均衡器是否相同if(balancer1==balancer2&&balance2==balancer3&&balancer3==balancer4){system.out.println(“服务器负载均衡器具有唯一性”);}61//本页接上页内容//以下增加服务器balancer1.addServer(“server1”);//增加服务器balancer1.addServer(“server2”);balancer1.addServer(“server3”);balancer1.addServer(“server4”);//以下模拟客户端请求的分发活动for(inti=0;i<10;i++){Stringserver=balancer1.getServer();system.out.println(“分发请求至服务器:”+server);}}}//虽然创建了4个LoadBalancer对象,但是他们实际上是同一//个对象(理论上似乎没有问题)。

62----负载均衡器的设计实际使用中的问题:以上程序在低速的“仿真”环境中运行时没有任何异常。实际低速运行时,当负载均衡器在启动过程中用户再次启动负载均衡器时,系统无任何异常。但高速并发环境下当客户端提交申请时出现请求分发失败,通过仔细分析发现原来系统中还是存在多个负载均衡器实例对象,导致分发时目标服务器不一致,从而产生冲突。这是为什么呢?问题原因:执行代码newLoadBalancer()创建较复杂对象的过程中需要进行大量的初始化工作,需要一段时间来完成,而在此时服务器是高速运行状态,若再一次调用一个getLoadBalancer()方法来执行newLoadBalancer()创建对象,由于上一个实例没有创建成功,这样将导致最终创建多个实例对象,这违背了单例模式的初衷,导致系统运行发生运行错误。如何解决这个问题??63负载均衡器的设计

----饿汉式单例与懒汉式单例的讨论(或资源抢占式)饿汉式单例类的设计:在定义静态变量时直接实例化单例类,因此在类加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例就将被创建。publicclassLoadBalancer{//以下私有静态实例变量privatestaticfinalLoadBalancerinstance=newLoadBalancer();privateLoadBalancer(){…}//私有初始化窗口//以下方法为公有静态成员,返回唯一实例。publicstaticLoadBalancergetInstance(){returninstance;}}那么,观察上述示例程序,饿汉式单例类设计有什么问题?64负载均衡器的设计

----饿汉式单例与懒汉式单例的讨论饿汉式单例类的优点:在类加载时就将自己实例化,无需考虑多线程访问问题,可以确保实例的唯一性;单例的创建时调用速度比较快。饿汉式单例类的缺点:该方式不能实现单例类的延迟加载,不管将来使用与否,该方式始终占用资源,这样的实例对象在大型软件系统里面如果过多的话,会影响系统性能。问题:大型软件系统设计中单例类一般要求延迟加载(用到的时候再加载,不用时想办法卸载)以节省系统资源。65负载均衡器的设计

----饿汉式单例与懒汉式单例的讨论懒汉式单例类与线程锁定:懒汉式单例在第一次调用getInstance()方法时实例化,在类加载时并不自行实例化,这种技术成为“延迟加载(LazyLoad)技术”;另外为了避免多个线程同时调用getInstance()方法,可以使用关键字synchronized用以线程锁定(多线程下,按访问顺序优先锁定第一个线程)。publicclassLoadBalancer{privatestaticLoadBalancerinstance=null;//私有静态变量privateLoadBalancer(){…}//私有初始化窗口//以下方法为公有静态成员,返回唯一实例。synchronizedpublicstaticLoadBalancergetInstance(){if(instance==null){instance=newLoadBalancer();}returninstance;}}66负载均衡器的设计

----饿汉式单例与懒汉式单例的讨论对上述懒汉式单例类设计的评价:

每次调用getInstance()方法时都需要进行线程锁定判断(占用一段时间),在多线程高并发访问环境中,将会导致系统性能大大降低。继续改造如下:无需对整个getInstance()方法进行锁定,只需要锁定代码“instance=newLoadBalancer();”即可。//以下方法为公有静态成员,返回唯一实例。publicstaticLoadBalancergetInstance(){if(instance==null){synchronized(LoadBalancer.class){instance=newLoadBalancer();}}returninstance;}67负载均衡器的设计

----饿汉式单例与懒汉式单例的讨论对上页懒汉式单例类设计的评价:

问题貌似得以解决,但事实并非如此。如果使用以上代码来创建单例对象,还是会存在单例对象不唯一,为什么?假设线程A和线程B都在调用getInstance()方法,此时Instance对象为

null,均能通过“instance==null”的判断,由于实现了synchronized加锁机制,线程A进入锁定的代码中创建实例,线程B处于排队等待状态。当线程A执行完毕后,线程B并不知道实例已经创建(已经提前判断过了,此时此处只等创建对象!),导致产生多个实例对象。

因此需要进一步改进。68负载均衡器的设计

----饿汉式单例与懒汉式单例的讨论继续改造如下:在synchronized锁定代码中再进行一次“instance==null”判断,这种方式成为“双重检查锁定(Double-CheckLocking)”,示例代码如下:

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论