




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
面向模式的软件体系架构钟玮军2014年3月Agenda概述-简要介绍模式的概念、地位、构成、分类以及描述方法。设计原则-设计原则是设计目标的高度抽象,模式是实现这些原则的具体方法。设计模式
-主要介绍了GOF23种软件设计模式。架构模式
-主要介绍了架构设计的几种模式。练习参考资料
-培训只能入门,需要在日常工作中积累提高,请保持阅读习惯。概述模式是什么模式是指从生产经验和生活经验中经过抽象和升华提炼出来的核心知识体系。模式其实就是解决某一类问题的方法论。把解决某类问题的方法总结归纳到理论高度,那就是模式。模式是一种指导,在一个良好的指导下,有助于你完成任务,有助于你作出一个优良的设计方案,达到事半功倍的效果。而且,会得到解决问题的最佳办法。模式在知识理念中的地位抽象的(abstract)具体的(concrete)模式的构成Context(语境)设计问题产生的上下文背景Problem(问题)给定语境中重复出现的问题
--解决方案必须满足的需求
--你必须考虑的约束
--解决方案必须具有期望的特性Solution(解决方案)针对该问题,证实有效的解决方案
--规定了一个特定的结构
--规定了运行期间的行为模式的作用一个模式关注一个在特定设计环境中出现的重复设计问题各种模式用文档方式记录现有的、经充分考验的设计经验模式明确并指明处于单个类和实例层次或组件层次之上的抽象模式为设计原则提供一种公共的词汇和理解模式是为软件体系结构建立文档的一种手段模式支持用已定义的属性来构造软件模式有助于建立一个复杂的和异构的软件体系结构模式有助于管理软件复杂度一个软件体系结构的模式描述了一个出现在特定设计语境中的特殊的再现设计问题,并为它的解决方案提供了一个经过充分验证的通用图式。解决方案图式通过描述其组成组件、它们的责任和相互关系以及它们的协作方式来具体指定。模式的层次分类架构模式(ArchitectureStyles)--是系统的高层次策略
,涉及到大尺度的组件以及整体性质--可作为具体软件体系结构的模板,是开发一个软件系统时的基本设计决策--规定了系统范围结构特性,架构模式的好坏影响到总体布局和框架性结构设计模式(DesignPatterns)--是中等尺度的结构策略。实现了一些大尺度组件的行为和它们之间的关系--模式的好坏不会影响到系统的总体布局和总体框架--设计模式定义出子系统或组件的微观结构代码模式(Idioms)--是特定的范例和与特定语言有关的编程技巧
--处理特定设计问题的实现,关注设计和实现方面--模式的好坏会影响中等尺度组件的内部、外部的结构或行为的底层细节模式描述名称:模式的名称和一个简短的摘要别名:模式的其他名称,如果知道的话例子:用来说明问题存在和需要模式的一个真实世界的例子语境:模式可以应用的情形问题:模式解决的问题,包括其相关强制条件的讨论解决方案:以该模式为基础的基本解决方案原理结构:模式结构方面的详细的规格说明动态特性:描述模式运行期间行为的典型场景实现:实现模式的指南已解决的例子:针对解决没有包括在上述小节中例子的一些重要方面讨论变体:模式变体或特例的简短描述已知使用:从已存在的系统中给出模式使用的例子效果:模式提供的优点和模式存在的潜在不足参见:参考那些解决相似问题的模式,并且参考其他一些模式,有助细化设计原则参考:《DesignPrinciplesandDesignPatterns》,RobertC.Martin/resources/articles/Principles_and_Patterns.pdf面向对象设计原则(1)-SOLID*开放封闭原则(OCP,OpenClosedPrinciple)模块应该对扩展开放,对更改封闭依赖倒置原则(DIP,DependencyInversionPrinciple)
依赖抽象,不要依赖实现;高层模块(稳定)不应该依赖于底层模块(变化),二者都应该依赖于抽象;抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象单一职责原则(SRP,SingleResponsibilityPrinciple)一个类应该仅有一个引起它变化的原因,应该只有一个职责;每一个职责都是变化的一个轴线,如果一个类有多个职责,这些职责就耦合在了一起Liskov替换原则(LSP,LiskovSubstitutionPrinciple)子类必须能够替换它们的基类(IS-A);如果调用的是父类的话,那么换成子类也完全可以运行接口隔离原则(ISP,InterfaceSegregationPrinciple)每一个接口应该是一种角色;多个客户特定的接口强于一个通用目的的接口,不应该强迫客户程序依赖它们不用的方法面向对象设计原则(2)*最少知识原则(迪米特法则)一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。一个软件实体应当尽可能少的与其他实体发生相互作用;每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。针对接口编程,而不是针对实现编程客户无需知道所使用对象的特定类型,只需要知道对象拥有客户所期望的接口优先使用对象组合,而不是类继承类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。继承在某种程度上破坏了封装性,子类父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。封装变化点使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。面向对象设计原则(3:包结构原则)TheReleaseReuseEquivalencyPrinciple(REP)
Thegranuleofreuseisthegranuleofrelease(软件包的粒度与可重用的粒度保持一致)TheCommonClosurePrinciple(CCP)Classesthatchangetogether,belongtogether(一致变化的类,应该属于同一个包)TheCommonReusePrinciple(CRP)Classesthataren’treusedtogethershouldnotbegroupedtogether(不被一起重用的类,不放到一个包里)高内聚(Cohesion)面向对象设计原则(4:包结构原则)TheAcyclicDependenciesPrinciple(ADP)Thedependenciesbetweenpackagesmustnotformcycles(软件包之间不能相互依赖,构成依赖循环)TheStableDependenciesPrinciple(SDP)Dependinthedirectionofstability(包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具稳定性)TheStableAbstractionsPrinciple(SAP)Stablepackagesshouldbeabstractpackages(稳定的包,应该是抽象的包,以此带来可变性)低耦合(Coupling)设计模式(DesignPatterns)设计模式起源
《DesignPatterns:ElementsofReusableObject-OrientedSoftware》,GOF23种设计模式。发展增加了一些类别,最重要的是使涵盖范围扩展到更具体的问题类型。例如:-《PatternsinJava》增加了解决涉及诸如并发等问题的模式,-《CoreJ2EEPatterns》主要关注使用Java2企业技术的多层应用程序上的模式。GOF-23设计模式分类从目的来看创建型模式(Creational):
将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。结构型模式(Structural):通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击。行为型模式(Behavioral):通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击。从范围来看类模式处理类与子类的静态关系对象模式处理对象间的动态关系GOF-23设计模式概览创建型结构型行为型类FactoryMethodAdapter_ClassInterpreter
TemplateMethod对象AbstractFactory
Builder
Prototype
SingletonAdapter_Object
Bridge
Composite
Decorator
Facade
Flyweight
ProxyChainofResponsibility
Command
Iterator
Mediator
Memento
Observer
State
Strategy
Visitor从封装变化角度对模式分类对象创建FactoryMethodAbstractFactoryPrototypeBuilder组件协作TemplateMethodObserver/EventStrategy单一职责DecoratorBridge对象性能SingletonFlyweight接口隔离FacadeProxyMediatorAdaptor状态变化MementoState数据结构CompositeIteratorChainofResponsibility行为变化CommandVisitor领域问题Interpreter来源:祝成科技软件开发培训“对象创建”模式通过“对象创建”模式对象绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。典型模式FactoryMethodAbstractFactoryPrototypeBuilderFactoryMethod模式举例
你是一个ATM机软件的开发工程师,需要测试ATM软件的存取款功能,但在测试时不想产生实际的银行业务,该怎么办呢?publicclassATMGui{……privateStatusdoWithdrawal(Accountaccount,floatamount){Transactiontransaction=newTransaction();transaction.setSourceAccount(account);transaction.setDestAccount(myCashAccount());transaction.setAmount(amount);cess();if(transaction.successful()){dispense(amount);}returntransaction.getStatus();
}}PublicclassATMGuiTest{
……publicvoidtestCheckingWithdrawal(){floatstartingBalance=balanceForTestCheckingAccount();AtmGuiatm=newAtmGui();insertCardAndInputPin(atm);atm.pressButton("Withdraw");atm.pressButton("Checking");atm.pressButtons("1","0","0","0","0");assertContains("$100.00",atm.getDisplayContents());atm.pressButton("Continue");assertEquals(startingBalance-100,balanceForTestCheckingAccount());
}}FactoryMethod模式FactoryMethod模式动机在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?意图定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使得一个类的实例化延迟到子类FactoryMethod模式结构适用性当一个类不知道它所必须创建的对象的类的时候当一个类希望由它的子类来指定它所创建的对象的时候当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同TemplateMethod,具有实际的逻辑和意义该类型对客户端是否隐藏呢?AbstractFactory模式举例Java可以根据变成人员的要求改变界面风格,如Windows风格,Java风格等,这是怎么做到的呢?AbstractFactory模式AbstractFactory模式动机在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?意图提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。AbstractFactory模式结构适用性一个系统要独立于它的产品的创建、组合和表示时。一个系统要由多个产品系列中的一个来配置时。当你要强调一系列相关的产品对象的设计以便进行联合使用时。当你提供一个产品类库,而只想显示它们的接口而不是实现时。主要用于应对“新系列”的需求变动。缺点在于难以应对“新对象”的需求变动Prototype模式举例
细胞通常具有非常复杂的结构。在生物工程模拟细胞分裂时,如何快速实现从一个细胞到两个细胞的创建工作?Prototype模式动机在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?意图使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。Prototype模式结构适用性当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者为了避免创建一个与产品类层次平行的工厂类层次时;或者当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。Builder模式举例ProtocolBuffer中,要实现协议类的快速序列化与反序列化,在协议类的实现中封装了非常复杂的数据结构,而ProtocolBuffer的使用者,不希望用很复杂的方法来创建该协议类。messageRequest{//RPCservicefullnamerequiredstringservice_name=1;
//RPCmethodnamerequiredstringmethod_name=2;
//RPCrequestprotorequiredbytesrequest_proto=3;}Request.Builderbuilder=Request.newBuilder();builder.setServiceName(…);builder.setMethodName(…);builder.setRequestProto(…);Requestrequest=builder.build();Builder模式场景在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。Builder模式动机在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?意图将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。Builder模式结构适用性当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。当构造过程必须允许被构造的对象有不同的表示时。(变化点在哪里,封装在哪里)主要在于“分步骤构建一个复杂的对象”。其中“分步骤”是一个稳定的算法,复杂对象的各个部分则经常变化。缺点在于难以应对“分步骤构建算法”的需求变动。“组件协作”模式现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。典型模式TemplateMethodObserver/EventStrategyTemplateMethod模式举例对于写Servlet的开发人员,常常只需要指定如何处理Get、Post、Put等几个少数的方法就可以了,而对于一些复杂的处理过程,如把字节流转换为ServletRequest对象,或从字节流中识别不同请求类型并分发到合适的处理方法,可以一概都不需要知道。publicabstractclassHttpServletextendsGenericServletimplementsjava.io.Serializable{
protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp) throwsServletException,IOException{//略…}
protectedvoidservice(HttpServletRequestreq,
HttpServletResponseresp)throwsServletException,IOException{
Stringmethod=req.getMethod();//取得請求的方法
if(method.equals(METHOD_GET)){//HTTPGET
//略...
doGet(req,resp);
//略...
}elseif(method.equals(METHOD_HEAD)){//HTTPHEAD
//略...
doHead(req,resp);
}elseif(method.equals(METHOD_POST)){//HTTPPOST
//略...
doPost(req,resp);
}elseif(method.equals(METHOD_PUT)){//HTTPPUT
//略...
}}
TemplateMethod模式动机在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?意图定义一个操作中的算法的骨架(稳定),而将一些步骤(变化)延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义(override重写)该算法的某些特定步骤。TemplateMethod模式结构适用性一次性实现一个算法的不变的部分,将可变的行为留给子类来实现。各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。是“重分解以一般化”的一个很好的例子。控制子类扩展。模板方法只在特定点调用“hook”操作(参见效果一节),这样就只允许在这些点进行扩展。被TemplateMethod调用的虚方法一般推荐设置为protected。Observer/Event模式举例JavaNIO非堵塞技术是如何实现的?以下是采用Netty创建服务端的代码片段。publicclassHelloServer{
publicstaticvoidmain(Stringargs[]){
//Server服务启动器
ServerBootstrapbootstrap=newServerBootstrap(…);
//设置一个处理客户端消息和各种消息事件的类(Handler)
bootstrap
.setPipelineFactory(newChannelPipelineFactory(){
@Override
publicChannelPipelinegetPipeline()
throwsException{
returnChannels
.pipeline(newHelloServerHandler());
}
});
//开放8000端口供客户端访问。
bootstrap.bind(newInetSocketAddress(8000));
}
privatestaticclassHelloServerHandlerextends
SimpleChannelHandler{
//当有客户端绑定到服务端的时候触发,打印"Helloworld,I'mserver."
@Override
publicvoidchannelConnected(ChannelHandlerContextctx,
ChannelStateEvente){
System.out.println("Helloworld,I'mserver.");
}
}}Observer/Event模式动机在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”,即一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。意图定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新Observer/Event模式结构适用性当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。Strategy模式举例负载均衡是分布式计算中常见的问题,实现负载均衡有很多不同算法,如随机算法、RoundRobin算法、一致性Hash算法等。(1)运维人员可能根据需要随时调整负载均衡策略;(2)将来可能有更新更好的负载均衡算法。我们应该如何设计我们的分布式框架?
ProtectedNodegetNode(LoadBalanceTypetype){
if(RANDOM==type){
returngetRandomNode();
}elseif(ROUNDROBIN==type){
returngetRoundRobinNode();
}elseif…
}Strategy模式动机在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?意图定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。Strategy模式结构适用性“策略”及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法间进行切换。提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。“单一职责”模式在软件组件的设计中”,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。典型模式DecoratorBridgeDecorator模式举例SiteMesh是由一个基于Web页面布局、装饰以及与现存Web应用整合的框架。它能帮助我们在由大量页面构成的项目中创建一致的页面布局和外观,如一致的导航条,一致的banner,一致的版权,等等。它不仅仅能处理动态的内容,如jsp,php,asp等产生的内容,它也能处理静态的内容,如htm的内容,使得它的内容也符合你的页面结构的要求。甚至于它能将HTML文件象include那样将该文件作为一个面板的形式嵌入到别的文件中去。所有的这些,都是GOF的Decorator模式的最生动的实现。尽管它是由java语言来实现的,但它能与其他Web应用很好地集成。Decorator模式动机在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?意图动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。Decorator模式结构适用性在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。处理那些可以撤消的职责。当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。并非解决“多子类衍生的多继承”问题,应用的要点在于解决“主体类在多个方向上的扩展能力”——是为“装饰”的含义Bridge模式举例手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高。这时候我们应该怎么办?(如果更改品牌或增加软件都会增加很多的变动)两种方式的结构如下:手机这个例子可能很直观,想想关于状态的例子?当我们需要各种不同状态的复杂组合时,考虑一下Bridge模式吧。Bridge模式Bridge模式动机由于某些类型的固有逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?意图将抽象部分与实现部分分离,使它们都可以独立地变化。Bridge模式结构适用性使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。应用一般在“两个非常强的变化强度”“对象性能”模式面向对象很好地解决了“抽象”的问题,但是必不可免地要付出一定的代价。对于通常情况来讲,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。典型模式SingletonFlyweightSingleton模式举例每一个Java程序实际上都是启动了一个JVM进程。我们如何在Java程序中获取当前JVM的状态呢?Runtime类封装了运行时的环境。每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接。一般不能实例化一个Runtime对象,应用程序也不能创建自己的Runtime类实例,但可以通过getRuntime方法获取当前Runtime运行时对象的引用。一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。Runtimerun=Runtime.getRuntime();System.out.println(“JVM最大内存:”+run.maxMemory());System.out.println(“JVM空闲内存:”+run.freeMemory());Singleton模式动机在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?这应该是类设计者的责任,而不是使用者的责任。意图保证一个类仅有一个实例,并提供一个该实例的全局访问点。Singleton模式结构适用性当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。实例构造器可以设置为protected以允许子类派生;一般不要支持序列化和ICloneable接口,因为有可能导致多个对象实例。不能应对多线程环境:在多线程环境下,仍然有可能得到多个实例对象(需要加信号量)Flyweight模式举例字符串在大多数应用程序中的出现频率较高,Java必须对字符串的执行进行优化以保证效率。执行以下代码,看看结果和你预期的是否一致?publicclassStringTest{publicstaticvoidmain(String[]args){Stringfly="fly",weight="weight";Stringfly2="fly",weight2="weight";System.out.println(fly==fly2);//?
System.out.println(weight==weight2);//?StringdistinctString=fly+weight;System.out.println(distinctString=="flyweight");//?Stringflyweight=(fly+weight).intern();System.out.println(flyweight=="flyweight");//?}}Flyweight的另一个典型例子是JavaSwing。请查阅相关资料,指出Swing树的速度是如何做到这么快的。(秘密在TreeCellRenderer.getTreeCellRendererComponent()里)Flyweight模式动机采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?意图运用共享技术有效地支持大量细粒度的对象。Flyweight模式结构适用性主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。采用对象共享的做法降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。具体实现时,要注意对象状态的处理。对象的大多数状态都可变为外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?需要根据具体应用情况进行评估,而不能凭空臆断。“接口隔离”模式在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接接口,来隔离本来互相紧密关联的接口是一种常见的解决方案。典型模式FacadeProxyMediatorAdaptorFacade模式举例汽车启动时,电瓶中的电能转化成起动机机械能,从而由曲轴带动发动机转动,同时点火、喷油在缸内燃烧,作工。发动机是一种能量转换机构,它将燃料燃烧产生的热能转变成机械能,发动机的一个工作循环包括四个过程:进气、压缩、作功、排气。发动机有很多种,如四行程汽油机、二行程汽油机等,工作机制又各有不同,…其实说了这么多,开车司机点火的时候所要做的就是,把点火钥匙插入钥匙孔,轻轻一转就可以了。Facade模式动机上述A方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战。如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?意图为子系统中的一组接口提供一个一致的界面,Façade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。A方案B方案Facade模式结构适用性简化了整个组件系统的接口,同时对于组件内部与外部客户程序来说,从某种程度上也达到了一种“解耦”的效果——内部子系统的任何变化不会影响到Façade接口的变化。更注重从架构的层次去看整个系统,而不是单个类的层次并非一个集装箱,可以任意地放进任何多个对象。Façade模式中组件的内部应该是“相互耦合关系比较大的一系列组件”,而不是一个简单的功能集合。Proxy模式举例(1)远程代理(RemoteProxy)(2)虚代理(VirtualProxy)(3)保护代理(ProtectionProxy)(4)智能指针(SmartReference)Proxy模式动机在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式。意图为其他对象提供一种代理以控制对这个对象的访问。Proxy模式结构适用性在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。具体实现方法、实现粒度都相差很大。可能针对单个对象也可能针对组件模块做proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损失一些透明性是可以接受的Mediator模式举例试想繁忙的首都国际机场,每天进出港航班达上千次,如果没有机场飞行控制系统(就是传说中的塔台)是难以想象的。塔台具有绝对的权利,他可以控制任何一架飞机的起飞和降落时间以及地方,而飞机和飞机之间不允许通信。同事关系中介者Mediator模式动机在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。意图用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。Mediator模式结构适用性一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。想定制一个分布在多个类中的行为,而又不想生成太多的子类。将多个对象间复杂的关联关系解耦,将多个对象间的控制逻辑进行集中管理。Façade是解耦系统外到系统内(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。随着控制逻辑的复杂化,可能需要对Mediator具体对象进行分解处理。Adapter模式举例电源插座在不同国家有多种标准,如国标、美标、英标、南非标、德标、意标等,此外各国生活用电电压标准也各有不同,如美国的生活用电电压是110V,而中国的电压是220V。如果要在中国使用美国电器,你有什么好主意吗?我们有多款OBD设备,协议都各不相同,并且在某些项目中客户还不排除引入第三方厂商的OBD硬件,我们的OBD核心平台应该采取什么样的设计模式呢?Adapter模式动机在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?意图将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。Adapter模式结构适用性你想使用一个已经存在的类,而它的接口不符合你的需求。你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。(仅适用于对象Adaptor)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。Adapter模式本身要求我们尽可能地使用“面向接口的编程风格”,这样才能在后期很方便地适配。“对象”适配器推荐“状态变化”模式在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一问题提供了一种解决方案。典型模式MementoStateMemento模式举例我们玩单机游戏的时候总会遇到MM大人的各种事情,一会儿陪逛街,一会儿去打个酱油,会耽误我们玩游戏的进程,但是此时我们能有“保存游戏”这个宝贝,我们的主基地就不会在我们打酱油的时候被对手拆掉。“保存游戏”的功能其实就是备忘录模式的很好应用,它在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以把该对象恢复到原先保存的状态。你能想到其它类似的例子吗?Memento模式动机在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。意图在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。Memento模式结构适用性备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。适用于“由原发器管理,却又必须存储在原发器之外的信息”要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口,一个为其他对象使用的窄接口现代语言(如C#、Java等)往往采用效率较高,又较容易正确实现的序列化方案来实现Memento模式。State模式举例实现G.8032(EthernetRingProtection)协议。State模式动机在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?意图允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。State模式结构适用性一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。为不同状态引入不同对象使得状态转换变得更加明确,保证转换原子性“数据结构”模式常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大地破坏组件的复用。这时候,将这些特定数据结构封装在内部,在外部提供统一的接口,来实现与特定数据结构无关的访问,是一种行之有效的解决方案。典型模式CompositeIteratorChainofResponsibilityComposite模式举例你该如何对车网互联的组织架构进行建模呢?Composite模式动机在某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?意图将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。Composite模式结构适用性采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。将“客户代码与复杂的对象容器结构”解耦是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能“应对变化”。具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。Iterator模式举例Java集合类有Collection,List,Set,Map等,虽然每种类的细节各有不同,但对于集合元素的遍历需求是一致的。public
class
SimpleCollection{
public
static
void
main(String[]args){
Collection<Integer>c=new
ArrayList<Integer>();
for(int
i=0;i<10;i++)
c.add(i);
//Autoboxing
for(Integeri:c)
System.out.println(i+",");
}}Iterator模式动机在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。意图提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。Iterator模式结构适用性迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。支持对聚合对象的多种遍历。ChainofResponsibility模式举例以下web.xml看着眼熟吗?Axis等很多系统也有相同的设计,使得编程人员可以灵活增删和定义自己的处理规则。<filter>myFilter</filter><filter-name>myFilter</filter-name><filter-class>xx.MyFilter</filter-class></filter><filter-mapping><filter-name>myFilter</filter-name><servlet-name>目标资源一</servlet-name><dispatcher>REQUEST</dispatcher></filter-mapping><filter-mapping><filter-name>myFilter</filter-name><servlet-name>目标资源二</servlet-name><dispatcher>FORWARD</dispatcher></filter-mapping>ChainofResponsibility模式动机在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。意图使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。ChainofResponsibility模式结构适用性应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。“行为变化”模式在组件的构建过程中,组件行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者之间的松耦合。典型模式CommandVisitorCommand模式举例在设计界面时,大家可以注意到这样的一种情况,同样的菜单控件,在不同的应用环境中的功能是完全不同的;而菜单选项的某个功能可能和鼠标右键的某个功能完全一致。按照最差、最原始的设计,这些不同功能的菜单、或者右键弹出菜单是要分开来实现的,你可以想象一下,word文档上面的一排菜单要实现出多少个“形似神非”的菜单类来?这完全是行不通的。这时,就要运用分离变化与不变的因素,将菜单触发的功能分离出来,而制作菜单的时候只是提供一个统一的触发接口。这样修改设计后,功能点可以被不同的菜单或者右键重用;而且菜单控件也可以去除变化因素,很大的提高了重用;而且分离了显示逻辑和业务逻辑的耦合。这便是命令模式的雏形。Command模式动机在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销/重(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。意图将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。Command模式结构适用性根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”。具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息;可以将多个“命令”封装为一个“复合命令”MacroCommand。与C++函数对象区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格,更符合抽象原则;C++函数对象以函数签名来定义行为接口规范,更灵活,更高效,但抽象能力比较弱。Visitor模式举例一个集合(Collection)中,可以包含一个Car,也可以包含一个Cat,对于不同类型的元素,他们的行为也不尽相同,比如,Car可能有start()行为,而Cat可能有eat()的行为。可是对于Collection来说,不管你是Car,还是Cat,取出来的都是Object,那么我们如何知道取出来的是什么呢?我们可能会如下操作:Iteratoritor=collection.iterator();while(itor.hasNext()){
Objecto=itor.next();
if(oinstanceofCar){
((Car)o).start();
}elseif(oinstanceofCat){
((Cat)o).eat();
}}
这时你应该可以看出这种方式可能会出现的问题,假如现在Collection中放入了成千上万个不同类型的对象,也就意味着你需要成千上万个ifelse。这时,我们是否需要考量一下,我们的设计…Visitor模式动机在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?意图表示一个作用于某对象结构中的各元素的操作。使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。Visitor模式结构适用性通过所谓双重分发(doubledispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。所谓双重分发即Visitor模式中间包括了两个多态分发:第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。适用于“类层次结构稳定,而其中操作经常面临频繁改动”。“领域问题”模式在特定领域中,某些变化的频度已经超出了传统的“抽象接口”所能解决的范围。这时候,结合特定领域,分析领域特点,从而给出在该领域下的一般性解决方案。典型模式InterpreterInterpreter模式举例正则表达式;规则引擎;自动化测试工具;医疗项目中对于医院上传Excel文件的内容正确性校验;……Interpreter模式动机在软件构建过程中,如果某一特定领域的问题比较复杂,类似的结构不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。意图给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。Interpreter模式结构适用性应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。其它设计模式结构化分解整体-部分模式工作的组织主控-从属模式访问控制代理模式管理命令处理器视图处理程序模式通信转发器-接收器模式客户机-分配器-服务器模式出版者-订阅者模式事件通道《面向模式的软件体系结构–卷1模式系统》整体-部分模式意图有助于聚合组件形成一种语义单元。一种聚合组件(即整体),封装其构成组件(即部分),组织它们之间的协作,并为其功能提供一个公共接口。直接访问各个部分是不可能的。结构关联Composite模式是整体-部分模式的一个变体Façade模式并不强行封装部分-客户机也可以直接访问部分主控-从属模式意图支持容错性、并行计算以及计算准确性。主控组件将工作分配给相同的从属组件并从从属组件返回的结果中计算出最终的结果。结构关联ChainofResponsibilityCommandMediator代理模式(访问控制)意图使一个组件的客户机与一个组件代表而不是组件本身通信,引入这样一个占位符有许多用途,包括提高效率、易于存取和防止越权访问。结构关联FacadeIterator命令处理器模式意图将一个服务的请求与其执行分开。一个命令处理器组件管理作为独立对象的请求,调度它们的执行,并且提供额外的服务,例如存储以后撤销用的请求对象。结构关联CommandMementoViewHandler模式意图有助于管理软件系统提供的所有视图。一个视图处理程序组件使得客户机能够打开、使用和关闭视图。它还协调视图间的依赖关系并组织它们的更新结构Forward-Receiver模式意图为对等交互模型的软件系统提供了透明的进程间通信。它引入转发器和接收器用于从底层通信机制中分离出对等体。结构Client-Dispatcher-Server模式意图在客户机与服务器间引入一个中间层——分配器组件。借助命名服务实现位置透明性,并且隐藏客户机与服务器间建立通信连接的细节。结构事件通道模式意图由OMG在其事件服务规格说明中提出并且以分布式系统为目标。严格分理出出版者和订阅者。订阅者不关心到底是哪个组件的数据被更改了,出版者也不关心哪个组件正在订阅。结构更多设计模式…?架构模式(ArchitectureStyles)经典的架构模式(分类)类别名称例子调用-返回
(CallandReturn)分层
(HierarchicalLayer)OSI7层网络协议主程序-子程序
(mainprogramandsubroutine)很多面向对象(OOsystems)很多独立组件
(Independentcomponents)通讯进程(communicatingprocesses)某些自控系统事件系统(eventsystems)OS微内核结构
MFCmessage机制虚拟机
(VirtualMachine)解释器(interpreters)c++compiler
任天堂模拟器onPC
cpu软件模拟器for嵌入式开发规则为中心(rule-basedsystems)某些人工智能系统数据流
(Dataflow)批处理(batchsequential)某些老式的os管道-过滤器(pipesandfilters)MSDirectShow
UnixCC(compiler/linker等)数据为中心
(Data-centered)数据库(database)DB2
Oracle黑板(blackboard)某些人工智能系统架构模式的现代分类类别名称特点例子从混沌到结构(FromMudtoStructure)分层(Layer).直观的分而治之方式
.层的重用
.依赖性局部化
.可替代性通信协议栈
Java虚拟机
很多应用系统管道-过滤器
(PipesandFilters).重用性好
.便于重组新策略
.交互性差
.适用于复杂处理编译器
UnixShell
DirectShow
通信协议解析黑板(Blackboard).算法和数据分离
.耦合度大
.适用于某些AI系统人工智能系统分布式系统
(DistributedSystem)代理者
(Broker).屏蔽复杂性
.支持互操作
.适用于分布式系统CORBA
RMI
交叉编译系统交互式系统(InteractiveSystem)MVC
(Model-View-Controller).灵活性
.广泛流行很多PAC
(Presentation-Abstraction-Control).灵活性
.复杂人工智能系统适应性系统(AdaptiveSystem)微内核
(Microkernel).适应变化
.支持长生命周期
.复杂OS
JBoss
Eclipse基于元数据
(Meta-Level-Architecture).适应变化
.支持长生命周期
.不易实现反射框架
Spring
MDA支持把整个系统任务以受控方式分解成可写作的子任务为分布式应用提供了一个完整的基础架构(涉及到微核、管道和过滤器模式)支持具有人机交互特征的软件系统的构建支持应用的扩展以及他们对进化技术和变更功能需求的适应性《面向模式的软件体系结构–卷1模式系统》常见架构模式DistributedEvent-drivenFrame-basedBatchPipesandfiltersRepository-centricBlackboardInterpreterRule-basedLayeredMVCIR-centricSubsumptionDisposal“从混沌到结构”模式支持把整个系统任务以受控方式分解成可写作的子任务典型模式LayersPipesandFiltersBalckboardLayers模式Example:OSI7-LayerModelBenefits:Aidingdevelopmentbyteams(多团队并行开发)Incrementalcodingandtesting(增量编码与测试)Easierexchangeofindividualparts(易于替换独立部分)Reuseofindividuallayersindifferentlayersindifferentcontexts(可以在不同场景下复用不同分层)Layers模式Context:Alargesystemthatrequiresdecomposition(一个需要分解的大系统)Problem:Latesourcecodechangesshouldnotripplet
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 国际关系学院《工程力学与机械设计》2023-2024学年第二学期期末试卷
- 河北环境工程学院《护理学基础技术(一)》2023-2024学年第二学期期末试卷
- 南京航空航天大学金城学院《细胞生物学课程设计》2023-2024学年第二学期期末试卷
- 广州城市职业学院《战略管理》2023-2024学年第二学期期末试卷
- 广东新安职业技术学院《生物化学及实验》2023-2024学年第二学期期末试卷
- 长春师范大学《汽车底盘构造与维修》2023-2024学年第二学期期末试卷
- 山西华澳商贸职业学院《移动通信技术》2023-2024学年第二学期期末试卷
- 大学生毕业实习计划
- 大一新生军训心得感悟(28篇)
- 农村乱占耕地建房问题整治工作汇报范文(3篇)
- 外研社一起英语四年级下册课文
- 学校办公室主任述职报告
- 《列夫·托尔斯泰》-完整版PPT
- 高考古代诗歌鉴赏复习教案
- 负数的认识1202
- After-Effects影视特效设计教程完整版全套ppt课件
- 中国铁塔建设维护工作培训PPT通用通用课件
- 新视野大学英语第三版Book 2 Unit 1 Text A
- 医疗设备清单
- SHD干燥机说明书(英)
- 蓝色卡通风格研学旅行报告PPT讲座学习
评论
0/150
提交评论