精华内容
下载资源
问答
  • SOA面向服务架构详解
    万次阅读
    2018-11-11 20:31:17

    一、定义介绍

           SOA(Service-Oriented Architecture,面向服务的架构)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。

           SOA是一种粗粒度、松耦合服务架构服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。SOA可以看作是B/S模型、XML标准通用标记语言的子集)/Web Service技术之后的自然延伸。

           SOA将能够帮助软件工程师们站在一个新的高度理解企业级架构中的各种组件的开发、部署形式,它将帮助企业系统架构者以更迅速、更可靠、更具重用性架构整个业务系统。较之以往,以SOA架构的系统能够更加从容地面对业务的急剧变化

    二、体系结构

    松耦合的系统

           这种具有中立的接口定义(没有强制绑定到特定的实现上)的特征称为服务之间的松耦合。松耦合系统的好处有两点,一点是它的灵活性,另一点是,当组成整个应用程序的每个服务的内部结构和实现逐渐地发生改变时,它能够继续存在。与之相反,紧耦合意味着应用程序的不同组件之间的接口与其功能和结构是紧密相连的,因而当需要对部分或整个应用程序进行某种形式的更改时,它们就显得非常脆弱。

           对松耦合的系统的需要来源于业务应用程序需要根据业务的需要变得更加灵活,以适应不断变化的环境,比如经常改变的政策、业务级别、业务重点、合作伙伴关系、行业地位以及其他与业务有关的因素,这些因素甚至会影响业务的性质。我们称能够灵活地适应环境变化的业务为按需(On demand)业务,在按需业务中,一旦需要,就可以对完成或执行任务的方式进行必要的更改。

           虽然面向服务的体系结构不是一个新鲜事物,但它却是更传统的面向对象的模型的替代模型,面向对象的模型是紧耦合的,已经存在二十多年了。虽然基于 SOA 的系统并不排除使用面向对象的设计来构建单个服务,但是其整体设计却是面向服务的。由于它考虑到了系统内的对象,所以虽然 SOA 是基于对象的,但是作为一个整体,它却不是面向对象的。不同之处在于接口本身。SOA 系统原型的一个典型例子是通用对象请求代理体系结构(Common Object Request Broker Architecture,CORBA),它已经出现很长时间了,其定义的概念与 SOA 相似。

           然而, SOA 已经有所不同了,因为它依赖于一些更新的进展,这些进展是以可扩展标记语言(eXtensible Markup Language,XML)为基础的。通过使用基于XML标准通用标记语言的子集) 的语言(称为 Web 服务描述语言(Web Services Definition Language,WSDL))来描述接口,服务已经转到更动态且更灵活的接口系统中,非以前 CORBA 中的接口描述语言(Interface Definition Language,IDL)可比了。

           Web 服务并不是实现 SOA 的惟一方式。前面刚讲的 CORBA 是另一种方式,这样就有了面向消息的中间件(Message-Oriented Middleware)系统,比如 IBM 的 MQseries。但是为了建立体系结构模型,您所需要的并不只是服务描述。您需要定义整个应用程序如何在服务之间执行其工作流。您尤其需要找到业务的操作和业务中所使用的软件的操作之间的转换点。因此,SOA 应该能够将业务的商业流程与它们的技术流程联系起来,并且映射这两者之间的关系。例如,给供应商付款的操作是商业流程,而更新您的零件数据库,以包括进新供应的货物却是技术流程。因而,工作流还可以在 SOA 的设计中扮演重要的角色。

           此外,动态业务的工作流不仅可以包括部门之间的操作,甚至还可以包括与不为您控制的外部合作伙伴进行的操作。因此,为了提高效率,您需要定义应该如何得知服务之间的关系的策略,这种策略常常采用服务级协定和操作策略的形式。

           最后,所有这些都必须处于一个信任和可靠的环境之中,以同预期的一样根据约定的条款来执行流程。因此,安全、信任和可靠的消息传递应该在任何 SOA 中都起着重要的作用。

    三、体系结构作用

           我可以用面向服务的体系结构做什么?

           对 SOA 的需要来源于需要使业务 IT 系统变得更加灵活,以适应业务中的改变。通过允许强定义的关系和依然灵活的特定实现,IT 系统既可以利用现有系统的功能,又可以准备在以后做一些改变来满足它们之间交互的需要。

           下面举一个具体的例子。一个服装零售组织拥有 500 家国际连锁店,它们常常需要更改设计来赶上时尚的潮流。这可能意味着不仅需要更改样式和颜色,甚至还可能需要更换布料、制造商和可交付的产品。如果零售商和制造商之间的系统不兼容,那么从一个供应商到另一个供应商的更换可能就是一个非常复杂的软件流程。通过利用 WSDL 接口在操作方面的灵活性,每个公司都可以将它们的现有系统保持现状,而仅仅匹配 WSDL 接口并制订新的服务级协定,这样就不必完全重构它们的软件系统了。这是业务的水平改变,也就是说,它们改变的是合作伙伴,而所有的业务操作基本上都保持不变。这里,业务接口可以作少许改变,而内部操作却不需要改变,之所以这样做,仅仅是为了能够与外部合作伙伴一起工作。

           另一种形式是内部改变,在这种改变中,零售组织决定它还将把连锁零售商店内的一些地方出租给专卖流行衣服的小商店,这可以看作是采用店中店(store-in-store)的业务模型。这里,虽然公司的大多数业务操作都保持不变,但是它们需要新的内部软件来处理这样的出租安排。尽管在内部软件系统可以承受全面的检修,但是它们需要在这样做的同时不会对与现有的供应商系统的交互产生大的影响。在这种情况下,SOA 模型保持原封不动,而内部实现却发生了变化。虽然可以将新的方面添加到 SOA 模型中来加入新的出租安排的职责,但是正常的零售管理系统继续如往常一样。

           为了延续内部改变的观念,IT 经理可能会发现,软件的新配置还可以以另外的一种方式加以使用,比如出租粘贴海报的地方以供广告之用。这里,新的业务提议是通过在新的设计中重用灵活的 SOA 模型得出的。这是来自 SOA 模型的新成果,并且还是一个新的机会,而这样的新机会在以前可能是不会有的。

           垂直改变也是可能的,在这种改变中,零售商从销售他们自己的服装完全转变到专门通过店中店模型出租地方。如果垂直改变完全从最底层开始的话,就会带来 SOA 模型结构的显著改变,与之一起改变的还可能有新的系统、软件、流程以及关系。在这种情况下,SOA 模型的好处是它从业务操作和流程的角度考虑问题而不是从应用程序和程序的角度考虑问题,这使得业务管理可以根据业务的操作清楚地确定什么需要添加、修改或删除。然后可以将软件系统构造为适合业务处理的方式,而不是在许多现有的软件平台上常常看到的其他方式。

           正如您可以看到的,在这里,改变和 SOA 系统适应改变的能力是最重要的部分。对于开发人员来说,这样的改变无论是在他们工作的范围之内还是在他们工作的范围之外都有可能发生,这取决于是否有改变需要知道接口是如何定义的以及它们相互之间如何进行交互。与开发人员不同的是,架构师的作用就是引起对 SOA 模型大的改变。这种分工,就是让开发人员集中精力于创建作为服务定义的功能单元,而让架构师和建模人员集中精力于如何将这些单元适当地组织在一起,它已经有十多年的历史了,通常用统一建模语言(Unified Modeling Language,UML),并且描述成模型驱动的体系结构(Model-Driven Architecture,MDA)。

           对于面向同步和异步应用的,基于请求/响应模式的分布式计算来说,SOA是一场革命。一个应用程序的业务逻辑(business logic)或某些单独的功能被模块化并作为服务呈现给消费者或客户端。这些服务的关键是他们的松耦合特性。例如,服务的接口和实现相独立。应用开发人员或者系统集成者可以通过组合一个或多个服务来构建应用,而无须理解服务的底层实现。举例来说,一个服务可以用.NET或J2EE来实现,而使用该服务的应用程序可以在不同的平台之上,使用的语言也可以不同。.

    四、特性状况

    基本特征

           SOA的实施具有几个鲜明的基本特征。实施SOA的关键目标是实现企业IT资产的最大化作用。要实现这一目标,就要在实施SOA的过程中牢记以下特征:

    (1)可从企业外部访问

    (2)随时可用

    (3)粗粒度的服务接口分级

    (4)松散耦合

    (5)可重用的服务

    (6)服务接口设计管理

    (7)标准化的服务接口

    (8)支持各种消息模式

    (9)精确定义的服务契约

           SOA服务具有平台独立的自我描述XML文档。Web服务描述语言(WSDL, Web Services Description Language)是用于描述服务的标准语言。

           SOA 服务用消息进行通信,该消息通常使用XML Schema来定义(也叫做XSD, XML Schema Definition)。消费者和提供者或消费者和服务之间的通信多见于不知道提供者的环境中。服务间的通讯也可以看作企业内部处理的关键商业文档。

           在一个企业内部,SOA服务通过一个扮演目录列表(directory listing)角色的登记处(Registry)来进行维护应用程序在登记处(Registry)寻找并调用某项服务。统一描述,定义和集成(UDDI, Universal Description, Definition, and Integration)是服务登记的标准。

           每项SOA服务都有一个与之相关的服务品质(QoS, quality of service)。QoS的一些关键元素有安全需求(例如认证和授权),可靠通信(译注:可靠消息是指确保消息“仅且仅仅”发送一次,从而过滤重复信息。),以及谁能调用服务的策略。

    五、新兴变革

    随着全球信息化的浪潮,信息化产业不断发展、延伸,已经深入了众多的企业及个人,SOA系统架构的出现,将给信息化带来一场新的革命。 

           纵观信息化建设与应用的历程,尽管出现过XML标准通用标记语言的子集)、Unicode、UML等众多信息标准,但是许多异构系统之间的数据源仍然使用各自独立的数据格式、元数据以及元模型,这是信息产品提供商一直以来形成的习惯。各个相对独立的源数据集成一起,往往通过构建一定的数据获取与计算程序来实现,这样的做法需要花费大量工作。信息孤岛大量存在的事实,使信息化建设的ROI(投资回报率)大大降低,ETL成为集中这些异构数据的有效工具。 ETL常用于从源系统中提取数据,将数据转换为与目标系统相兼容的格式,然后将其装载到目标系统中。数据经过获取、转换、装载后,要产生应用价值,还需另外的数据展现工具予以实现,如此复杂的数据应用过程,必定产生高昂的应用成本。

           结构化的数据管理尚可通过以上方法,予以实现其集成应用。在非结构化的内容方面,这些具有挑战性的问题令人生畏。内容管理的应用方案基于不同的信息化应用系统,而且大部分是纵向的以组织部门为界限的。在内容管理市场中,经常使用来自不同厂商的产品来提供这些解决方案。即使是同一个厂商的产品,相互之间的功能也是经常重叠,并且无法集成。

           随着信息化建设的深入,不同应用系统之间的功能界限已趋于模糊。同时企业资源计划系统和协同商务系统,又需要商业智能的分析展现数据提供用户操作依据。

           在激烈竞争且多变的市场环境下,企业的管理模式很难固化,应用传统的信息化软件,当企业要做出一些改动时需要面对巨大的挑战。

    SOA系统架构的出现,信息化变革

           微软大中华区服务部总经理辛儿伦介绍说,从上世纪60年代应用于主机的大型主机系统,到80年代应用于PC的CS 架构,一直到90年代互联网的出现,系统越来越朝小型化和分布式发展。2000年WebService出现后,SOA被誉为下一代Web服务的基础框架,已经成为计算机信息领域的一个新的发展方向。

           SOA的出现给传统的信息化产业带来新的概念,不再是各自独立的架构形式,能够轻松的互相联系组合共享信息

           可复用以往的信息化软件。基于SOA的协同软件提供了应用集成功能,能够将ERP、CRM、HR等异构系统的数据集成。

           松散耦合方式,只要充分了解业务的进程,就可以不用编写一行代码,通过流程图实现一套我们自己的信息系统。就像已经给你准备好了砖瓦和水泥,只需要想好盖什么样的房子就可以轻松的盖起。加快开发速度,并且减少了开发和维护的费用。软件将所有的管理提炼成表单和流程,以记录管理的内容,指定过程的流转方向。

           更简便的信息和数据集成。信息集成功能可以将散落在广域网和局域网上的文档、目录、网页轻松集成,加强了信息的协同相关性。同时,复杂、成本高昂的数据集成,也变成了可以简单且低成本实现的参数设定。创建了完全集成的信息化应用新领域。

           在具体的功能实现上,SOA协同软件所实现的功能包括了知识管理、流程管理、人事管理、客户管理、项目管理、应用集成等,从部门角度看涉及了行政、后勤、营销、物流、生产等。从应用思想上看,SOA协同软件中的信息管理功能,全面兼顾了贯穿整个企业组织的信息化软硬件投入。尽管各种IT技术可以用于不同的用途,但是信息管理并没有任意地将信息分为结构化或者非结构化的部分,因此ERP等结构化管理系统并不是信息化建设的全部;同时,信息管理也没有将信息化解决方案划分为部门的视图,因此仅仅以部分为界限去构建软件应用功能的思想未必是不可撼动的。基于SOA的协同软件与 ERP、CRM等传统应用软件相比,关键的不同在于它可以在合适的时间、合适的地点并且有正当理由向需要它提供服务的任何用户提供服务。

    六、为何选择SOA

    简介介绍

           不同种类的操作系统,应用软件,系统软件和应用基础结构(application infrastructure)相互交织,这便是IT企业的现状。一些现存的应用程序被用来处理当前的业务流程(business processes),因此从头建立一个新的基础环境是不可能的。企业应该能对业务的变化做出快速的反应,利用对现有的应用程序和应用基础结构(application infrastructure)的投资来解决新的业务需求,为客户,商业伙伴以及供应商提供新的互动渠道,并呈现一个可以支持有机业务(organic business)的构架。SOA凭借其松耦合的特性,使得企业可以按照模块化的方式来添加新服务或更新现有服务,以解决新的业务需要,提供选择从而可以通过不同的渠道提供服务,并可以把企业现有的或已有的应用作为服务, 从而保护了现有的IT基础建设投资。

           一个使用SOA的企业,可以使用一组现有的应用来创建一个供应链复合应用(supply chain composite application),这些现有的应用通过标准接口来提供功能。

    服务架构

    为了实现SOA,企业需要一个服务架构,下图显示了一个例子:

    在上图中, 服务消费者(service consumer)可以通过发送消息来调用服务。这些消息由一个服务总线(service bus)转换后发送给适当的服务实现。这种服务架构可以提供一个业务规则引(business rules engine),该引擎容许业务规则被合并在一个服务里或多个服务里。这种架构也提供了一个服务管理基础(service management infrastructure),用来管理服务,类似审核,列表(billing),日志等功能。此外,该架构给企业提供了灵活的业务流程,更好地处理控制请求(regulatory requirement),例如Sarbanes Oxley(SOX),并且可以在不影响其他服务的情况下更改某项服务。

    基础结构

    要运行,管理SOA应用程序,企业需要SOA基础,这是SOA平台的一个部分。SOA基础必须支持所有的相关标准,和需要的运行时容器。下图所示的是一个典型的SOA基础结构。

    SOAP, WSDL, UDDI

    WSDL,UDDI和SOAP是SOA基础的基础部件。WSDL用来描述服务;UDDI用来注册和查找服务;而SOAP,作为传输层,用来在消费者和服务提供者之间传送消息。SOAP是Web服务的默认机制,其他的技术为可以服务实现其他类型的绑定。一个消费者可以在UDDI注册表(registry)查找服务,取得服务的WSDL描述,然后通过SOAP来调用服务

    WS-I Basic Profile

    WS-I Basic Profile,由Web服务互用性组织(Web Services Interoperability Organization)提供,是SOA服务测试与互用性所需要的核心构件。服务提供者可以使用Basic Profile测试程序来测试服务在不同平台和技术上的互用性。

    J2EE 和 .Net

    尽管J2EE和.NET平台是开发SOA应用程序常用的平台,但SOA不仅限于此。像J2EE这类平台,不仅为开发者自然而然地参与到SOA中来提供了一个平台,还通过他们内在的特性,将可扩展性,可靠性,可用性以及性能引入了SOA世界。新的规范,例如 JAXB(Java API for XML Binding),用于将XML文档定位到Java类;JAXR(Java API for XML Registry)用来规范对UDDI注册表(registry)的操作;XML-RPC(Java API for XML-based Remote Procedure Call)在J2EE1.4中用来调用远程服务,这使得开发和部署可移植于标准J2EE容器的Web服务变得容易,与此同时,实现了跨平台(如.NET)的服务互用。

    服务品质

    在企业中,关键任务系统(mission-critical system,译注:关键任务系统是指如果一个系统的可靠性对于一个组织是至关重要的,那么该系统就是该企业的关键任务系统。比如,电话系统对于一个电话促销企业来说就是关键任务系统,而文字处理系统就不那么关键了。)用来解决高级需求,例如安全性,可靠性,事物。当一个企业开始采用服务架构作为工具来进行开发和部署应用的时候,基本的Web服务规范,像WSDL,SOAP,以及UDDI就不能满足这些高级需求。正如前面所提到的,这些需求也称作服务品质(QoS,quality of services)。与QoS相关的众多规范已经由一些标准化组织(standards bodies)提出,像W3C(World Wide Web Consortium)和OASIS(the Organization for the Advancement of Structured Information Standards)。下面的部分将会讨论一些QoS服务和相关标准。

    安全质量

    Web服务安全规范用来保证消息的安全性。该规范主要包括认证交换, 消息完整性和消息保密。该规范吸引人的地方在于它借助现有的安全标准,例如,SAML(as Security Assertion Markup Language)来实现web服务消息的安全。OASIS正致力于Web服务安全规范的制定。

    可靠信度

    在典型的SOA 环境中,服务消费者和服务提供者之间会有几种不同的文档在进行交换。具有诸如“仅且仅仅传送一次”( once-and-only-once delivery),“最多传送一次”( at-most-once delivery),“重复消息过滤”(duplicate message elimination),“保证消息传送”(guaranteed message delivery)等特性消息的发送和确认,在关键任务系统(mission-critical systems)中变得十分重要。WS-Reliability 和 WS-ReliableMessaging是两个用来解决此类问题的标准。这些标准都由OASIS负责。

    策略计划

    服务提供者有时候会要求服务消费者与某种策略通信。比如,服务提供商可能会要求消费者提供Kerberos安全标示,才能取得某项服务。这些要求被定义为策略断言(policy assertions)。一项策略可能会包含多个断言。WS-Policy用来标准化服务消费者和服务提供者之间的策略通信。

    控制能力

    当企业着手于服务架构时,服务可以用来整合数据仓库(silos of data),应用程序,以及组件。整合应用意味着例如异步通信,并行处理,数据转换,以及校正等进程请求必须被标准化。在SOA中,进程是使用一组离散的服务创建的。BPEL4WS 或者 WSBPEL(Web Service Business Process Execution Language)是用来控制这些服务的语言。WSBPEL也由OASIS负责。

    管理能力

    随着企业服务的增长,所使用的服务和业务进程的数量也随之增加,一个用来让系统管理员管理所有运行在多相环境下的服务的管理系统就显得尤为重要。WSDM(Web Services for Distributed Management)规定了任何根据WSDM实现的服务都可以由一个WSDM适应(WSDM-compliant)的管理方案来管理。

    其它的qos特性,比如合作方之间的沟通和通讯,多个服务之间的事务处理,都在WS-Coordination 和 WS-Transaction 标准中描述, 这些都是OASIS 的工作。

    Web服务

    在理解SOA和Web服务的关系上,经常发生混淆。根据2003年4月的Gartner报道,Yefim V. Natis就这个问题是这样解释的:“Web服务是技术规范,而SOA是设计原则。特别是Web服务中的WSDL,是一个SOA配套的接口定义标准:这是Web服务和SOA的根本联系。”从本质上来说,SOA是一种架构模式,而Web服务是利用一组标准实现的服务。Web服务是实现SOA的方式之一。用Web服务来实现SOA的好处是你可以实现一个中立平台,来获得服务,而且随着越来越多的软件商支持越来越多的Web服务规范,你会取得更好的通用性。

    SOA优势

    SOA的概念并非什么新东西,SOA不同于现有的分布式技术之处在于大多数软件商接受它并有可以实现SOA的平台或应用程序。SOA伴随着无处不在的标准,为企业的现有资产或投资带来了更好的重用性。SOA能够在最新的和现有的应用之上创建应用;SOA能够使客户或服务消费者免予服务实现的改变所带来的影响;SOA能够升级单个服务或服务消费者而无需重写整个应用,也无需保留已经不再适用于新需求的现有系统。总而言之,SOA以借助现有的应用来组合产生新服务的敏捷方式,提供给企业更好的灵活性来构建应用程序和业务流程。

    发展效益

    A. 平衡最初的旧系统投资(Leverage initial investment):

    组织过去所投资的系统、软硬体,如果能再利用等于赋予其新的价值,这也替组织降低成本并增加竞争力。

    B. 基础建设的便利性(Infrastructure Commoditization):

    让所有的应用程式能相互沟通(互通性)。

    C. 快速的接近市场(Faster time-to-market):

    服务的重复使用(再利用),来缩短过去的组织流程,更快速的提供服务来接近市场。

    D. 减少支出(Reduce Cost):

    服务的重复使用,可降低开发成本。因为开发新系统的成本,大部份比更新旧有系统来的花费大。

    E. 减低风险(Risk mitigation):

    开发新系统的风险远大于更新旧系统。

    F. 持续改善商业流程的循环(Continuous improvement cycle for business process)

    G. 中心流程处理(Process-centric processing)

    主要优势

    一,SOA可通过互联网服务器发布,从而突破企业内网的限制,实现与供应链上下游伙伴业务的紧密结合。通过SOA架构,企业可以与其业务伙伴直接建立新渠道,建立新伙伴的成本得以降低。

    二,SOA与平台无关,减少了业务应用实现的限制。要将企业的业务伙伴整合到企业的“大”业务系统中,对其业务伙伴具体采用什么技术没有限制。

    三, SOA具有低耦合性特点,业务伙伴对整个业务系统的影响较低。在企业与各业务伙伴关系不断发生变化的情况下,节省的费用会越来越多。

    四, SOA具有可按模块分阶段进行实施的优势。可以成功一步再做下一步,将实施对企业的冲击减少到最小。

    五, SOA的实施可能并不具有成本显著性。这要分三种情况加以讨论:

    (1) 当企业从零开始构建业务系统时,采用SOA架构与不采用SOA架构成本可看做是相同的。

    (2) 当企业业务发展或发生企业重组等变化而原有系统不能满足需要,而需要重构业务系统时,采用SOA架构与不采用SOA架构成本可看做是相同的。

    (3) 当企业业务发生缓慢变化并可预见到将来需要重构业务系统时,由于可以按模块分阶段逐步实施SOA以适应变化的需要,这样企业不需一下投入一大笔经费进行系统改造,而是根据企业业务发展情况和资金情况逐步投入,缓解了信息投入的压力。

    推动因素

    IDC负责企业平台研究的副总裁Michelle Bailey说,最近的IDC的研究表明,到2011年,18%以上的全部新服务器都将采用虚拟化技术,对于服务器硬件供应商来说,这是一个年收入达220亿美元的市场机会。

    对于企业来说,日益增长的挑战是如何管理和保证虚拟环境的安全,因为随着机构采用虚拟化技术,传统的管理物理服务器蔓延的挑战正在转向管理虚拟机蔓延的挑战。机构将需要可靠的、稳定的、安全的和可管理的虚拟化解决方案。

    绿色IT一直被列为头号的战略技术和2008年大多数机构的趋势。据IDC称,虚拟化的绿色的好处不仅是减少服务器占地面积,而是还包括减少碳排放量和耗电量。这些好处正在成为重要的好处。

    据IDC对亚太地区绿色IT的调查,75%的受访者对于IT部门没有绿色IT政策。然而,80%以上的受访者认为他们的IT供应商的“绿色”在未来几年将更加重要。

    虚拟化在这方面将发挥重要作用,一些企业将采用更环保的方法经营业务以便赢得政府部门的合同。其它机构正在采用虚拟化技术以便得到节省电源的好处和减少碳排放量的奖励。

    同时,一些企业管理者和市场研究人士也对虚拟化的未来发展发表了看法:

    Avnet公司营销经理Michael Costigan:

    尽管虚拟化有巨大的潜力,许多转销商不知道这种有潜力的新技术的实际状况。机构能够获得显著的能量和计算效率,同时提高技术的应用率和灵活性。

    为了帮助你的客户认识到这些好处并且为你的企业建立强大的市场占有率,你需要了解这个强大的新技术的细节,了解需要采取什么有效手段识别和利用虚拟化的真正机会。

    虚拟化正在用来解决范围日益广泛的商业目标和挑战,如服务器整合/保留、业务持续性、测试/开发优化、软件开发与发布以及桌面管理和安全。

    人们对于虚拟化的未来显然非常感兴趣。但是,还有许多言过其实的宣传。第一波x86服务器虚拟化的应用一直集中在服务器整合方面,重点是减少资本开支 (也就是服务器开支)以及电源和冷却等运营开支。在未来的五年里,机构将超越服务器整合寻求如何利用虚拟化技术得到其它的好处,如重点减少运营成本(也就是物理管理成本)和让基础设施更有活力和更灵活,以便改善IT对于不断变化的商业需求的反应能力。

    分析师认为,虚拟化的下一个大事将是高可用性和灾难恢复工具。灾难恢复在历史上一直是非常难管理的。虚拟化将提供一个节省成本的和容易管理的灾难恢复解决方案。

    虚拟桌面基础设施、资源平衡和应用程序级高可用性可能是其它的未来应用实例。这些解决方案有一些技术的和经济的障碍。这些障碍必须要在虚拟化广泛应用前克服。但是,考虑到虚拟化的重点,这些障碍已经在开始克服。虚拟化还将成为SOA(面向服务的架构)技术应用的推动因素。 [4]  面向服务的体系结构基于这些实际活动或业务服务进行组织,而不是形成公司所维护的不同的信息竖井 (Silo)。通过实现 SOA,可以带来大量好处,包括以下各个方面:  *更高的业务和 IT 一致性

    *基于组件的系统

    *松散耦合的组件和系统

    *基于网络的基础设施,允许分散于各地且采用不同技术的资源协同工作

    *动态构建的按需应用程序

    *更高的代码重用率

    *更好地标准化整个企业内的流程

    *更易于集中企业控制

    七、优点

    服务导向架构并不是一种全新的解决方案;相反,SOA是技术与架构的自然进化。系统架构一直在不断进步,与商业保持高度一致。系统设计师与商家很早就认识到将技术与商业流程相协调的重要性,包括充分应用并合理化技术资源,以及为商业提供更好的支持。

    SOA也在一定程度上源于早已有之的企业架构理论。企业架构对技术进行评估,但是更重要的是,它关注整个企业和全部的商业流程并提供了做出技术决策的背景信息。SOA工具则融合了互联网技术,如HTTP和XML,以及综合技术,如消息总线、转译技术和连接技术。 

    更多相关内容
  • 品质部组织架构图及各岗位职责.pdf
  • 前言:今天在这里分享几个图谱,大家可以对照着几个图谱,看看自己会什么,能做什么,又有那些不足的地方,从而去提升自己,其次脑海里有一个全面的图谱,对于自己以后的...二、质量架构全景 三、质量效率全景 ...

    前言:今天在这里分享几个图谱,大家可以对照着几个图谱,看看自己会什么,能做什么,又有那些不足的地方,从而去提升自己,其次脑海里有一个全面的图谱,对于自己以后的职业规划也是有帮助的,至少你知道自己的工作职业原来是这么牛逼的,哈哈哈!

    一、QA质量体系搭建

    在这里插入图片描述

    二、质量架构全景在这里插入图片描述

    三、质量效率全景图

    在这里插入图片描述

    展开全文
  • 输出高品质MATLAB图形的方法与技巧.pdf
  • 看清滴滴出行组织架构迭代

    千次阅读 2020-12-20 00:00:00
    12月10日,滴滴出行发布全员信,宣布新一轮组织架构调整,将分散的业务进行整合成立新事业群,并进行了多名高管的换岗,据网经社不完全统计,截至目前滴滴已累计进行了8次组织架构调整。成立城市...

    12月10日,滴滴出行发布全员信,宣布新一轮组织架构调整,将分散的业务进行整合成立新事业群,并进行了多名高管的换岗,据网经社不完全统计,截至目前滴滴已累计进行了8次组织架构调整。

    成立城市运输与服务事业群 多名高管换岗

    全员信显示,将原两轮车、代驾、跑腿、货运业务合并为“城市运输与服务事业群”。任命付强担任城市运输与服务事业群CEO兼事业群安委会主任,向Will汇报。两轮车事业部总经理张治东、代驾事业部和货运事业部总经理赵辉,向付强汇报。任命孙枢担任网约车平台公司CEO兼网约车安委会主任以及花小猪打车总经理,向Will汇报。同时,任命杨峻担任小桔车服总经理兼车服安委会主任,向Will汇报。

    为了更好的支持橙心优选的发展,集团UT副总经理兼网约车平台乘客部总经理张可帅将调任橙心优选,集团UT副总经理兼平台产品负责人黄潇莹(小白)将兼任网约车平台乘客部总经理。此外,陈汀作为橙心优选CEO将专注于橙心优选的战斗中,不再兼任小桔车服总经理。

    全员信透露,以上的组织架构调整以及高管换岗是为了更好的适应下一阶段发展,实现“0188”战略目标。而据网经社了解,截至目前滴滴已累计进行了8次组织架构调整。

    2017年2月:五大战略正式公布

    2月16日,滴滴出行通过全员邮件宣布了2017年的五大战略关键词及全新升级的组织阵形。滴滴CEO程维和总裁柳青在邮件中表示,2017年滴滴的五大战略关键词为:修炼内功、智慧交通、专车决胜、全球布局、洪流落地。

    根据内部信,升级之后,滴滴内部将形成两大事业群,一个FT团队、多个事业部。两大事业群为快捷出行事业群和品质出行事业群。快捷出行事业群,下设出租车事业部、快车事业部、优步事业部、平台运营部和运力中心。

    作为今年滴滴五大战略的其中之一,新成立的智慧交通FT团队,下设公交事业部(原巴士事业部),由章文嵩领导。

    此外,在此次组织阵形升级中,滴滴成立了国际化事业部,由战略部负责人朱景士(Stephen)负责。

    2018年2月:落实两大战略调整

    2018年2月,滴滴宣布完成了新一轮的架构调整。包括两项战略调整:第一,继快捷出行事业群和品质出行事业群后,将战略部、国际业务事业部和金融业务事业部将合为战略事业群;第二,智慧交通团队会由虚入实,从智慧交通FT变成智慧交通事业部。

    成立的战略业务事业群包括了战略部、国际事业部、金融事业部三个部门。负责人为原战略部/国际业务负责人朱景士,同时他还继续兼任战略部和国际事业部负责人。

    新调整的智慧交通事业部,目的是为了解决智能交通问题,例如提供治拥堵方案、提高出行效率。该部门继续由原智慧交通FT负责人章文嵩负责。事业部又细分为:由刘向宏领导的城市交通业务;由章文嵩任GM、战略部刘西帝任AGM的公共交通业务以及由杨毅领导的车载设备业务。

    2018年下半年开始,滴滴进入了修整期,发展重点从速度和规模,变成了安全与合规。

    2018年12月:全面推进网约车合规化工作

    2018年12月5日,宣布新一轮组织架构调整,升级安全管理体系,同时组成网约车平台公司、车主服务公司,一个普惠出行服务事业群及多个部门协同发展的新型结构,以全力以赴投入用户安全、服务体验和出行效率优化建设。

    本次调整中,原快捷出行事业群、专车事业部、豪华车事业部合并成为网约车平台公司,全面推进网约车合规化进程。原小桔车服公司与汽车资产管理中心(AMC)则合并成立车主服务公司。原品质出行事业群的共享单车、电单车、代驾、企业级业务和原智慧交通事业部的公交业务则组成普惠出行与服务事业群。原快捷出行事业群出租车事业部由CTO 张博直接负责,持续加大出租车业务的产品技术资源的投入,促进出租车产品升级和新旧业态融合发展,进一步探索出租车与网约车融合新模式。

    2019年4月:升级安全产品与技术部架构

    2019年4月,滴滴出行宣布升级集团安全产品与技术部,升级后的安全产品与技术部下设信息安全部、业务安全部、基础安全产品部、安全研究部等多个部门。

    此外,滴滴任命弓峰敏为安全产品与技术部负责人,同时继续担任滴滴美国研究院院长;任命卜峥为滴滴美国研究院副院长,继续担任国际产品与技术部负责人。弓峰敏和卜峥继续向CTO张博汇报。

    2019年6月:整合升级成立两轮车事业部

    2019年6月17日晚,滴滴发布关于两轮车组织架构调整的内部邮件,邮件称,滴滴出行单车事业部(内部代号“海棠湾”)、电单车事业部(内部代号“黑马”)正式整合升级为两轮车事业部(内部代号为“海马”)。

    据了解,2018年初,为了打造一站式出行平台,滴滴出行推出了自有品牌“青桔单车”,由单车事业部负责运营;电单车事业部则主要负责运营另一个自有品牌“街兔电单车”,两个事业部同属普惠出行与服务事业群。

    2019年7月:“小桔车服”组织架构升级

    2019年7月,小桔车服进行组织架构升级,小桔租车升级为小桔租车平台,成立车企业务部和小桔能源业务板块,并升级小桔养车业务板块,以租车为主线,打造汽车、养车、能源“三大引擎”。

    新成立的车企业务部将整合小桔车服原有的车队资产管理、车企合作以及定制车业务。新成立的小桔能源将整合原有的充电与加油业务,持续在汽车能源领域开展合作。

    原有的汽车开放平台并入小桔租车平台,负责人为小桔车服副总经理杨峻。小桔租车平台继续坚持开放赋能,将从C端服务、商家赋能、商品管理等方面加强平台能力建设。

    2020年6月:滴滴升级出租车事业部组织架构

    2020年6月15日,滴滴出行公布出租车事业部组织架构升级及人事调整,任命石东海担任出租车事业部总经理,向CEO程维汇报,同时兼任普惠产品技术负责人,向CTO张博汇报。

    出租车事业部助理总经理兼出租车运营部负责人殷凯、出租车技术部负责人袁哲明、出租车产品部负责人隋信杰、出租车战略分析负责人徐雯向石东海汇报。此外,出租车运营部进行组织升级,下设安全与用户体验部、创新发展部、交易运营部、市场部、商业发展部。

    互联互通社区


    互联互通社区专注于IT互联网交流与学习,关注公众号:互联互通社区,每日获取最新报告并附带专题内容辅助学习。方案打造与宣讲、架构设计与执行、技术攻坚与培训、数据中台等技术咨询与服务合作请+微信:hulianhutongshequ

    展开全文
  • 品质管理资料大全.chm

    2020-04-05 20:45:18
    00品保组织架构.xls 01检验流程.xls 02.品质政策与质量目标.doc 03-1抽样检验作业办法.doc 03-2批检验记录表.doc 03-3字码和抽样方案表.xls 04-1首件及制程巡检办法.xls 04-2.首(末)件确认书.doc 05....
  • 三部曲 提升思维品质 撬动深度学习.pdf
  • 专业品质 设计助手——HP LaserJet 5200n助力设计CAD图纸输出.pdf
  • 领航品质,专业呈现——揭开惠普CAD矢量高质、高效打印的秘密.pdf
  • 佳能推出两款新型 大幅面喷墨打印机——满足从高品质图像到专业CAD输出的各类应用需求.pdf
  • 领航品质,专业呈现——揭开惠普CAD矢量高质、高效打印的秘密 (2).pdf
  • 领航品质,专业呈现——揭开惠普CAD矢量高质、高效打印的秘密 (1).pdf
  • Unity 3D 网络游戏架构设计

    千次阅读 多人点赞 2018-04-12 10:41:41
    本课程是 Unity 3D 系列教程,目标是带领读者搭建一个商业游戏的网络架构设计,该架构设计是游戏的核心技术,将采用 Unity 2017.2 最新版本作为开发工具。内容分为 UI 架构、技能架构、服务器和网络同步四大部分,共...

    课程简介

    本课程是 Unity 3D 系列教程,目标是带领读者搭建一个商业游戏的网络架构设计,该架构设计是游戏的核心技术,将采用 Unity 2017.2 最新版本作为开发工具。内容分为 UI 架构、技能架构、服务器和网络同步四大部分,共 13 篇文章。

    认真读完本系列文章之后,将会深入理解架构的设计,具备独立搭建网络游戏框架的能力,并在此基础上可以独立开发一款网络游戏。

    作者介绍

    姜雪伟,从事 IT 行业15年,现担任创业公司技术合伙人。著作有:《手把手教你架构 3D 游戏引擎》、《Unity 3D 实战核心技术详解》,《Cocos2d-x 3.x 图形学渲染技术讲解》等,参与或主导过十多款网络游戏研发。

    课程内容

    导读:网络游戏架构设计综述

    随着 Stream、TapTap 等游戏平台的崛起,越来越多的网络游戏在此平台投放,而且很多新发布的游戏收入都颇丰,这些发布的游戏很多都是几个人开发完成的,而且开发周期都比较短,如何才能快速开发网络游戏?一个比较好的游戏框架是非常必要的。另外,这些平台的崛起,对于独立游戏开发者来说,也是一个非常好的机会,换句话说独立开发者的春天又来了,当然对于那些想从事游戏开发或者说已经在这个行业从事游戏开发的人也是一个机会。

    现在游戏不只限于抄袭了,更强调创新,只要有好的创意,再加上一个比较好的游戏框架,几个志道同合的小伙伴就可以开发一款网络游戏。在国外有很多这方面的案例,几个人在不同的地方,一起开发一款游戏。而在国内很多普通程序员在游戏公司估计只是从事某项单一的逻辑功能编写,对整体架构设计并不是很了解,即使自己有好的想法局限于自己的能力估计也是很难做出一款游戏,在游戏公司很少有人会教你架构设计,而且对于开发者来说,要么只会客户端,要么只会服务器,非常少的人同时精通二者,这也困扰着那些想自己做游戏的开发者。本课程正是基于解决这些困扰程序员的问题,提出了一种基于网络服务器的架构设计,让程序员一个人可以同时进行客户端和服务器的网络游戏的开发,这样再加上美术和策划就可以搞定一款网络游戏。

    搭建游戏框架首先要搞清楚什么是框架?其实搭建框架的主要目的是便于游戏逻辑的编写,这样非常有利于开发者快速的开发游戏,框架的核心思想是模块之间的耦合性要降低。那我们先搞清楚游戏框架中主要包括哪些技术点,从大的方面说,每款游戏都有自己的 UI 系统、角色系统、技能系统、网络系统等等,往小的方面说就是编码的细节——每个类的编写。下面就把游戏中的几个核心系统的架构设计思想逐步介绍给读者,架构设计没有好坏之分,用着方便就可以,在这里就当是抛砖引玉,读者也可以在此基础上去扩展,去重新编写架构。这样本篇教程的目的就达到了。

    先介绍 UI 系统,这个是老生常谈的,UI 架构常用的设计模式是 MVC。读者应该对 MVC 都比较了解,原理就不介绍了,可以去网上查阅。下面讲下 MVC 模式如何在 UI 系统中使用?先看下面这幅架构图:

    UI框架设计

    我们就围绕着这幅图给读者介绍模块设计。

    在设计 UI 框架时首要考虑的事情

    首先,要做到 UI 资源和代码逻辑的分离,因为 UI 资源是经常更换的,如果二者不分离,很容易在更换资源时出现各种各样的脚本丢失以及资源和代码逻辑对应不上问题,这个对于程序来说必须要避免的,程序员不应该把时间都浪费在这些事情上面。

    其次,逻辑代码之间的耦合性要降低,降低耦合性的方法通过事件的方式进行处理,很多程序使用 SendMessage 这种 Unity 自带的消息发送机制,其实它是非常消耗 CPU 的,为了优化这些,我们会自己封装事件机制。

    以上两点是指导我们做架构的指导纲领,不论怎么设计最好围绕二者进行。

    接下来介绍 UI 架构搭建的各个逻辑模块,上图中显示的窗体模块并不全面,游戏中的窗体是非常多的,在此以登录窗体和英雄窗体为例进行说明:Loginwindow 和 HeroWindow 它们是负责显示的,也就是说,它对应具体的窗体逻辑,它对应的 MVC 模式中的 V,相当于 View 显示,该模块是不继承 Mono 的,也就是不挂接任何 UI 对象。LoginCtrl、HeroCtrl 模块相当于 MVC 中的 C,Control 控制,用于操作 LoginWindow,HeroWindow 所对应的 UI 窗体,比如用于控制不同窗体的显示、隐藏、删除等等操作,在图中没有列出 MVC 中的 Model 模块,这个模块主要是用于网络消息数据的接收,也可以通过文本文件直接赋值的,它可以使用列表进行存储,相对来说用处并不是不可替代的。

    游戏中存在的窗体是非常多的,这么多窗体,如果不同的开发者写逻辑,会搞的很多,不利于统一管理。由此需要一个类 WindowManager 管理类进行统一注册管理各个窗体类模块,这种处理方式也就我们经常说的工厂模式。

    另外,窗体之间是经常会进行不同的切换,这些切换也可以对它们进行流程管理,因为窗体之间的切换是一种固定的流程。既然经常转换,我们不免会想到状态机用于处理这种流程。在此,引入了状态机进行统一管理不同窗体状态的变换。各个模块之间的耦合性也是要重点考虑的问题,在此采用了自己封装的事件机制进行解耦合。

    具体实现逻辑如下,每个窗体对应自己的类,以登录 UI 为例进行说明,每个 UI 都是一个 Window 窗体对象,它对应着 Loginwindow 类、LoginCtrl 类、LoginState 类。其他的窗体类似,而这些类都不继承 Mono 也就是说不挂接到任何 UI 窗体对象上,这样,彻底实现了资源和代码的分离,UI 系统思想设计完成,接下来再介绍技能模块和角色系统的架构设计。

    技能模块设计思想

    技能模块在游戏中的表现非常重要,也是常见的,在实现之前先把技能设计架构给读者展示,如下图所示:

    enter image description here

    关于技能的设计,首先要考虑的是这个技能是谁释放的,也就是说的游戏实体类,实体类的设计在此分了三层:IEntity、IPlayer 和 Player,这三个模块同样不继承 Mono,也就是说不挂接到任何对象上,具体的实现会在后面的章节中结合代码详细介绍,技能释放者找到了,接下来设计技能了。

    游戏中的技能分好多种:正常释放的技能、被动技能、远程技能等等,这些不同的技能我们也将其进行模块化设计,其实它们的内容是类似的,可以考虑使用脚本自动生成代码。当然对于游戏中众多特效的使用,我们也需要写一个特效管理类,用于创建不同的特效,特效采用的就是模块化管理,特效实现了后,就要考虑特效是根据游戏实体对象的不同动作进行释放的,不同的动作对应着不同的技能,这当然就是不同动作之间的切换,在这里使用了 FSM 有限状态机进行统一调度。

    再介绍一个重要的模块——对象池,因为我们的特效会频繁的创建、销毁,还有游戏中的怪物 NPC 也是一样的。当然,其他的游戏管理类在游戏中都比较常见,其他的一些系统比如背包系统、任务系统,这些可以根据消息或者配置文件进行加载读取,这里就不一一说明了。

    接下来介绍比较重要的网络游戏服务器,我们的服务器使用的是 Photon Server,用户直接搭建非常方便,在本教程也会把服务器的搭建过程介绍给读者,我们的网络架构采用的是房间模式,同房间的人可以在场景中实时同步,包括技能、动作等等。而该实时同步的实现方式采用的是状态同步,接下来介绍一下 Photon 服务器的体系结构:

    enter image description here

    为什么选择 Photon Server 作为服务器,因为该服务器提供了负载均衡,以及做大型网络游戏 MMO 等技术实现,用户无需太关心。它的核心使用的是 C++ 编写的,效率无需使用者关心,同时该服务器支持 UDP、TCP、HTTP 和 Web 套接字,它的应用层使用的是 C# 编写的,对于用户编写逻辑非常方便,而且它也支持数据库和非数据库模式,比如 MySQL、SQL Server 等数据库,以及 MongoDB、Redis 等非数据库。

    再介绍一下关于服务器的基本工作流程,从客户端角度来看,工作流程也非常简单,非常适合新手学习,客户端连接到主服务器,可以加入大厅,并检索打开游戏列表。当他们在 Master 主服务器上 CreateGame 操作时,游戏实际上并不创建游戏服务器,而是确定人数比较少的游戏服务器,将 IP 地址返回给客户端。当客户端在主服务器上调用 JoinGame 或 JoinRandomGame 操作时,主服务器查找运行游戏的游戏服务器,并将其 IP 返回给客户端。流程图如下所示:

    enter image description here

    如果客户端与主服务器断开连接,使用刚收到的 IP 连接到游戏服务器,再次调用 CreateGame 或 JoinGame 操作,断线重连都没有任何问题。下面介绍游戏中比较重要的部分,MMO 游戏同步思想。

    关于使用 Photon Server 做 MMO 游戏同步实现的思想

    客户端中的地图,同样也会在服务器中虚拟一个跟客户端大小完全一样的地图,角色就是在这些虚拟空间中同步,角色同步是在一定的区域内进行同步的,也就是在一定的区域内是互相“看见”的,这种看见与客户端的相机裁剪是完全不同的。效果如下图所示:

    enter image description here

    计算哪些对象在某些区域会频繁移动,这些对象可能会非常耗费 CPU 资源。加速这一计算的一个简单的方法是将虚拟空间划分为固定区域,然后计算哪些区域重叠。客户应该接收这些重叠区域中的项目的所有事件。最简单的算法使用方形的网格,有时我们也称为九宫格算法,如下所示:

    enter image description here

    物体通过当前的区域推送事件,一旦特定的区域重叠,它自动订阅区域的事件通道,并开始接收包括物品推送的区域事件。为了避免在区域边界频繁地订阅和取消订阅改变,引入了另外的更大的兴趣区域半径:跨越此外半径的订阅区域被取消订阅,客户端停止接收区域事件。用通俗的语言讲就是在服务器虚拟的场景中,会通过不同的玩家生成各自的九宫格区域,其他 NPC 或者玩家在对方的九宫格区域里面,物体都会显示,离开自己的九宫格区域就剪掉,这样也会是考虑到效率问题,因为如果整个场景实时同步计算,这对于客户端和服务器压力都是很大的。九宫格区域如果重合那就把重合的部分都显示出来。如下图所示:

    enter image description here

    本教程实现的网络游戏架构设计,最终实现的效果图如下所示:

    enter image description here

    该图是简单的创建房间以及加入房间进行网络同步界面,进入游戏后实现的游戏中的效果如下图所示:

    enter image description here

    用户创建房间,其他用户加入房间,多人场景在同一房间中同步的效果如下所示:

    enter image description here

    通过此网络游戏框架可以快速的把网络游戏实现出来,本课程的最后会把服务器和客户端代码都奉献给读者,希望对开发者有所帮助。从下章开始,本教程进行详细介绍架构设计实现。

    第01课:游戏资源管理实现

    游戏中的资源量是必须要考虑的问题,游戏品质的好坏都是通过资源表现的,这些资源的管理,作为开发者必须要处理的。对于游戏资源管理,通常的做法是简单的封装几个接口用于资源的加载,如果只是做个 Demo,这样做是没问题的,但是如果做产品,对于资源的需求量是非常大的,而且各个资源的加载也会由于使用不当,出现各种问题,而且游戏讲究的是团队协作,不同的人会有不同的需求,简单的封装几个接口很难满足需求,如果没有一个统一的资源架构管理,代码会出现各种接口版本,最后会出现大量的冗余代码,这样对游戏产品运行效率会产生影响。

    另外,还要考虑游戏资源的动态加载更新,主要是为了减少游戏包体的大小,Unity3D 虽然为用户提供了 AssetBundle 资源打包,方便用户将资源打包上传到资源服务器,在游戏启动时会通过本地存放资源的 MD5 文本文件与服务器的保存资源最新的 MD5 码的文本文件作对比,根据其资源对应的 MD5 码不同,将新的资源下载到本地使用,同时将资源文件代替本地的资源文件。我们在封装资源管理类时,也是从产品的角度考虑资源管理问题。

    下面开始讲解如何进行资源管理的代码封装,我们对资源管理的封装做了一个比较完善的思考,代码模块如下图所示:

    enter image description here

    下面来告诉读者为什么这么设计。我们在游戏开发时,对于 Unity 资源,每个资源都是一个 GameObject,只是单独的 GameObject 显然不能满足需求,因为资源既可以是 Scene,也可以是 Prefab,同时也可以是 Asset 文件。这就会涉及到不同的资源类型,如何表示这些资源类型,比如我测试的时候可以使用 prefab,而在正式发布时采用 asset,如果不做分类,在游戏发布时还要修改接口,非常麻烦。但如果设计一个通用的接口,对于资源类型可以使用枚举进行表示,有了这些想法后,开始逐步去实施我们的思想。

    首先需要设计一个 ResourceUnit 模块,它是资源的基本单位,也是程序自己封装的资源基本单位,ResourceUnit 类的代码如下所示:

    public enum ResourceType{    ASSET,    PREFAB,    LEVELASSET,    LEVEL,}

    上面就是我们定义的资源枚举,每一个加载的资源都是一个 ResourceUnit,它可以是 assetbundle,可以是 prefab 实例化,当然也可以是 scene。下面继续完善 ResourceUnit 类,它的实现代码如下所示:

     public class ResourceUnit : IDisposable{    private string mPath;    private Object mAsset;    private ResourceType mResourceType;    private List<ResourceUnit> mNextLevelAssets;    private AssetBundle mAssetBundle;    private int mReferenceCount;    internal ResourceUnit(AssetBundle assetBundle, int assetBundleSize, Object asset, string path, ResourceType resourceType)    {        mPath = path;        mAsset = asset;        mResourceType = resourceType;        mNextLevelAssets = new List<ResourceUnit>();        mAssetBundle = assetBundle;        mAssetBundleSize = assetBundleSize;        mReferenceCount = 0;    }    public List<ResourceUnit> NextLevelAssets    {        get        {            return mNextLevelAssets;        }        internal set        {            foreach (ResourceUnit asset in value)            {                mNextLevelAssets.Add(asset);            }        }    }    public int ReferenceCount    {        get        {            return mReferenceCount;        }    }    //增加引用计数    public void addReferenceCount()    {        ++mReferenceCount;        foreach (ResourceUnit asset in mNextLevelAssets)        {            asset.addReferenceCount();        }    }    //减少引用计数    public void reduceReferenceCount()    {        --mReferenceCount;        foreach (ResourceUnit asset in mNextLevelAssets)        {            asset.reduceReferenceCount();        }        if (isCanDestory())        {            Dispose();        }    }    public bool isCanDestory() { return (0 == mReferenceCount); }    public void Dispose()    {        ResourceCommon.Log("Destory " + mPath);        if (null != mAssetBundle)        {            //mAssetBundle.Unload(true);            mAssetBundle = null;        }        mNextLevelAssets.Clear();        mAsset = null;    }}

    ResourceUnit 类同时实现了资源的引用计数,该设计思想跟内存的使用比较类似,这样便于程序知道对于加载的资源什么时候销毁,什么时候可以继续使用,它还声明了一些变量,比如资源的名字等。

    另外,程序要加载资源,首先要知道资源加载路径,其次要知道资源类型是 asset bundle 还是 prefab。我们通常会使用一个类专用于资源路径的设置,包括获取资源文件夹、资源路径、获取资源文件以及获取 AssetBundle 包体文件大小等等。该类的代码实现如下所示:

         public class ResourceCommon    {        public static string textFilePath = Application.streamingAssetsPath;        public static string assetbundleFilePath = Application.dataPath + "/assetbundles/";        public static string assetbundleFileSuffix = ".bytes";        public static string DEBUGTYPENAME = "Resource";        //根据资源路径获取资源名称        public static string getResourceName(string resPathName)        {            int index = resPathName.LastIndexOf("/");            if (index == -1)                return resPathName;            else            {                return resPathName.Substring(index + 1, resPathName.Length - index - 1);            }        }        //获取文件名字        public static string getFileName(string fileName)        {            int index = fileName.IndexOf(".");            if (-1 == index)                throw new Exception("can not find .!!!");            return fileName.Substring(0, index);        }        //获取文件名字        public static string getFileName(string filePath, bool suffix)        {            if (!suffix)            {                string path = filePath.Replace("\\", "/");                int index = path.LastIndexOf("/");                if (-1 == index)                    throw new Exception("can not find .!!!");                int index2 = path.LastIndexOf(".");                if (-1 == index2)                    throw new Exception("can not find /!!!");                return path.Substring(index + 1, index2 - index - 1);            }            else            {                string path = filePath.Replace("\\", "/");                int index = path.LastIndexOf("/");                if (-1 == index)                    throw new Exception("can not find /!!!");                return path.Substring(index + 1, path.Length - index - 1);            }        }        //获取文件夹        public static string getFolder(string path)        {            path = path.Replace("\\", "/");            int index = path.LastIndexOf("/");            if (-1 == index)                throw new Exception("can not find /!!!");            return path.Substring(index + 1, path.Length - index - 1);        }        //获取文件后缀        public static string getFileSuffix(string filePath)        {            int index = filePath.LastIndexOf(".");            if (-1 == index)                throw new Exception("can not find Suffix!!! the filePath is : " + filePath);            return filePath.Substring(index + 1, filePath.Length - index - 1);        }        //获取文件        public static void getFiles(string path, bool recursion, Dictionary<string, List<string>> allFiles, bool useSuffix, string suffix)        {            if (recursion)            {                string[] dirs = Directory.GetDirectories(path);                foreach (string dir in dirs)                {                    if (getFolder(dir) == ".svn")                        continue;                    getFiles(dir, recursion, allFiles, useSuffix, suffix);                }            }            string[] files = Directory.GetFiles(path);            foreach (string file in files)            {                string fileSuffix = getFileSuffix(file);                if (fileSuffix == "meta" || fileSuffix == "dll")                    continue;                if (useSuffix && fileSuffix != suffix)                    continue;                string relativePath = file.Replace("\\", "/");                relativePath = relativePath.Replace(Application.dataPath, "");                string fileName = getFileName(file, true);                if (allFiles.ContainsKey(fileName))                {                    allFiles[fileName].Add(relativePath);                }                else                {                    List<string> list = new List<string>();                    list.Add(relativePath);                    allFiles.Add(fileName, list);                }            }        }        //检查文件夹        public static void CheckFolder(string path)        {            if (!Directory.Exists(path))                Directory.CreateDirectory(path);        }        //获取文件路径        public static string getPath(string filePath)        {            string path = filePath.Replace("\\", "/");            int index = path.LastIndexOf("/");            if (-1 == index)                throw new Exception("can not find /!!!");            return path.Substring(0, index);        }        //获取本地路径        public static string getLocalPath(string path)        {            string localPath = string.Format("{0}/{1}", Application.persistentDataPath, path);            if (!File.Exists(localPath))            {                if (Application.platform == RuntimePlatform.Android)                {                    localPath = string.Format("{0}/{1}", Application.streamingAssetsPath, path);                }                else if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)                {                    localPath = string.Format("file://{0}/{1}", Application.streamingAssetsPath, path);                }                return localPath;            }            return "file:///" + localPath;        }        //获取AssetBundles文件字节        public static byte[] getAssetBundleFileBytes(string path, ref int size)        {            string localPath;            //Andrio跟IOS环境使用沙箱目录            if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)            {                localPath = string.Format("{0}/{1}", Application.persistentDataPath, path + ResourceCommon.assetbundleFileSuffix);            }            //Window下使用assetbunlde资源目录            else            {                localPath = ResourceCommon.assetbundleFilePath + path + ResourceCommon.assetbundleFileSuffix;            }            Debug.Log(localPath);            //首先检测沙箱目录中是否有更新资源                     if (File.Exists(localPath))            {                try                {                    FileStream bundleFile = File.Open(localPath, FileMode.Open, FileAccess.Read);                    byte[] bytes = new byte[bundleFile.Length];                    bundleFile.Read(bytes, 0, (int)bundleFile.Length);                    size = (int)bundleFile.Length;                    bundleFile.Close();                    return bytes;                }                catch (Exception e)                {                    Debug.LogError(e.Message);                    return null;                }            }            //原始包中            else            {                TextAsset bundleFile = Resources.Load(path) as TextAsset;                if (null == bundleFile)                    Debug.LogError("load : " + path + " bundleFile error!!!");                size = bundleFile.bytes.Length;                return bundleFile.bytes;            }        }    }}

    以上封装了资源模块通用的一些接口,便于我们在开发中使用。在游戏处理资源过程中,还需要考虑一个问题,程序在请求资源时,要知道资源是在加载过程中,还是已经卸载完成。在程序中会使用一个枚举值进行设置,用于通知程序资源的使用状态,同时会使用委托函数进行具体回调操作,比如资源加载完成,我要知道什么时候加载完成了。根据这些设想,我们用一个类把它实现出来,这个也就是 Request 类,代码实现如下:

        //资源请求类型    public enum RequestType    {        LOAD,        UNLOAD,        LOADLEVEL,        UNLOADLEVEL,    }    class Request    {        internal string mFileName;              //请求资源相对Assets/完整路径名称        internal ResourceType mResourceType;        //委托回调函数        internal ResourcesManager.HandleFinishLoad mHandle;        internal ResourcesManager.HandleFinishLoadLevel mHandleLevel;        internal ResourcesManager.HandleFinishUnLoadLevel mHandleUnloadLevel;        internal RequestType mRequestType;        internal ResourceAsyncOperation mResourceAsyncOperation;        //构造函数        internal Request(string fileName, ResourceType resourceType, ResourcesManager.HandleFinishLoad handle, RequestType requestType, ResourceAsyncOperation operation)        {            mFileName = fileName;            mResourceType = resourceType;            mHandle = handle;            mRequestType = requestType;            mResourceAsyncOperation = operation;        }        //构造函数        internal Request(string fileName, ResourceType resourceType, ResourcesManager.HandleFinishLoadLevel handle, RequestType requestType, ResourceAsyncOperation operation)        {            mFileName = fileName;            mResourceType = resourceType;            mHandleLevel = handle;            mRequestType = requestType;            mResourceAsyncOperation = operation;        }    }

    场景与场景之间进行切换过渡时,尤其对于比较大的资源加载,我们通常使用一个进度条进行过渡,为此在框架中封装了一个通用的资源过渡类,代码实现如下:

        public class ResourceAsyncOperation    {        internal RequestType mRequestType;        internal int mAllDependencesAssetSize;        internal int mLoadDependencesAssetSize;        internal bool mComplete;        public AsyncOperation asyncOperation;        internal ResourceUnit mResource;        internal ResourceAsyncOperation(RequestType requestType)        {            mRequestType = requestType;            mAllDependencesAssetSize = 0;            mLoadDependencesAssetSize = 0;            mComplete = false;            asyncOperation = null;            mResource = null;        }        public bool Complete        {            get            {                return mComplete;            }        }        //资源加载进度        public int Prograss        {            get            {                if (mComplete)                    return 100;                else if (0 == mLoadDependencesAssetSize)                    return 0;                else                {                    //使用assetbundle                    if (ResourcesManager.Instance.UsedAssetBundle)                    {                        if (RequestType.LOADLEVEL == mRequestType)                        {                            int depsPrograss = (int)(((float)mLoadDependencesAssetSize / mAllDependencesAssetSize) * 100);                            int levelPrograss = asyncOperation != null ? (int)((float)asyncOperation.progress * 100.0f) : 0;                            return (int)(depsPrograss * 0.8) + (int)(levelPrograss * 0.2);                        }                        else                        {                            return (int)(((float)mLoadDependencesAssetSize / mAllDependencesAssetSize) * 100);                        }                    }                    //不使用                    else                    {                        if (RequestType.LOADLEVEL == mRequestType)                        {                            int levelPrograss = asyncOperation != null ? (int)((float)asyncOperation.progress * 100.0f) : 0;                            return levelPrograss;                        }                        else                        {                            return 0;                        }                    }                }            }        }    }

    关于资源的架构思想,我们基本已经完成了,接下来就要考虑如何使用了,但不能直接使用它们,因为它们既不是单例,也不是静态类,它没有提供对外接口,那怎么办呢?这就要想到管理类,对,我们可以使用管理类提供对外的接口,也就是 ResourceManager 类,管理类是对外提供接口的,对于管理类,它通常是单例模式,我们把游戏中的单例分为两种:一种是继承 mono 的单例,一种是不继承 mono 的。我们设计的资源管理类是可以挂接到对象上的,这主要是为了资源更新时使用的。管理类它可以加载资源、销毁资源等等。它的内容实现代码如下:

         public class ResourcesManager : UnitySingleton<ResourcesManager>    {               //是否通过assetbundle加载资源        public bool UsedAssetBundle = false;        private bool mInit = false;        private int mFrameCount = 0;        private Request mCurrentRequest = null;        private Queue<Request> mAllRequests = new Queue<Request>();        //保存读取的Resource信息        //private AssetInfoManager mAssetInfoManager = null;        private Dictionary<string, string> mResources = new Dictionary<string, string>();        //加载的资源信息        private Dictionary<string, ResourceUnit> mLoadedResourceUnit = new Dictionary<string, ResourceUnit>();        public delegate void HandleFinishLoad(ResourceUnit resource);        public delegate void HandleFinishLoadLevel();        public delegate void HandleFinishUnLoadLevel();        private void Start()        {        }        public void Init()        {            mInit = true;        }        public void Update()        {            if (!mInit)                return;            if (null == mCurrentRequest && mAllRequests.Count > 0)                handleRequest();            ++mFrameCount;            if (mFrameCount == 300)            {                mFrameCount = 0;            }        }        private void handleRequest()        {            //使用assetbundle打包功能            if (UsedAssetBundle)            {                mCurrentRequest = mAllRequests.Dequeue();                //相对Asset的完整资源路径                string fileName = mCurrentRequest.mFileName;                switch (mCurrentRequest.mRequestType)                {                    case RequestType.LOAD:                        {                            switch (mCurrentRequest.mResourceType)                            {                                case ResourceType.ASSET:                                case ResourceType.PREFAB:                                    {                                        if (mLoadedResourceUnit.ContainsKey(fileName))                                        {      mCurrentRequest.mResourceAsyncOperation.mComplete = true;                                            mCurrentRequest.mResourceAsyncOperation.mResource = mLoadedResourceUnit[fileName] as ResourceUnit;          if (null != mCurrentRequest.mHandle)                                                mCurrentRequest.mHandle(mLoadedResourceUnit[fileName] as ResourceUnit);                                            handleResponse();                                        }            else            {            }         }         break;               case ResourceType.LEVELASSET:               {              }                 break;                case ResourceType.LEVEL:                {                    //                    }                  break;                            }                        }                        break;                    case RequestType.UNLOAD:                        {                            if (!mLoadedResourceUnit.ContainsKey(fileName))                                Debug.LogError("can not find " + fileName);                            else                            {                            }                            handleResponse();                        }                        break;                    case RequestType.LOADLEVEL:                        {                            StartCoroutine(_loadLevel(fileName, mCurrentRequest.mHandleLevel, ResourceType.LEVEL, mCurrentRequest.mResourceAsyncOperation));                        }                        break;                    case RequestType.UNLOADLEVEL:                        {                            if (!mLoadedResourceUnit.ContainsKey(fileName))                                Debug.LogError("can not find level " + fileName);                            else                            {                                if (null != mCurrentRequest.mHandleUnloadLevel)                                    mCurrentRequest.mHandleUnloadLevel();                            }                            handleResponse();                        }                        break;                }            }            //不使用打包            else            {                mCurrentRequest = mAllRequests.Dequeue();                switch (mCurrentRequest.mRequestType)                {                    case RequestType.LOAD:                        {                            switch (mCurrentRequest.mResourceType)                            {                                case ResourceType.ASSET:                                case ResourceType.PREFAB:                                    {                                        //暂时不处理,直接使用资源相对路径                                    }                                    break;                                case ResourceType.LEVELASSET:                                    {                                    }                                    break;                                case ResourceType.LEVEL:                                    {                                    }                                    break;                            }                        }                        break;                    case RequestType.UNLOAD:                        {                            handleResponse();                        }                        break;                    case RequestType.LOADLEVEL:                        {                            StartCoroutine(_loadLevel(mCurrentRequest.mFileName, mCurrentRequest.mHandleLevel, ResourceType.LEVEL, mCurrentRequest.mResourceAsyncOperation));                        }                        break;                    case RequestType.UNLOADLEVEL:                        {                            if (null != mCurrentRequest.mHandleUnloadLevel)                                mCurrentRequest.mHandleUnloadLevel();                            handleResponse();                        }                        break;                }            }        }        private void handleResponse()        {            mCurrentRequest = null;        }        //传入Resources下相对路径名称 例如Resources/Game/Effect1    传入Game/Effect1        public ResourceUnit loadImmediate(string filePathName, ResourceType resourceType, string archiveName = "Resources")        {            //使用assetbundle打包            if (UsedAssetBundle)            {                //添加Resource                string completePath = "Resources/" + filePathName;                //加载本身预制件                ResourceUnit unit = _LoadImmediate(completePath, resourceType);                return unit;            }            //不使用            else            {                Object asset = Resources.Load(filePathName);                ResourceUnit resource = new ResourceUnit(null, 0, asset, null, resourceType);                return resource;            }        }        //加载场景        public ResourceAsyncOperation loadLevel(string fileName, HandleFinishLoadLevel handle, string archiveName = "Level")        {            {                ResourceAsyncOperation operation = new ResourceAsyncOperation(RequestType.LOADLEVEL);                mAllRequests.Enqueue(new Request(fileName, ResourceType.LEVEL, handle, RequestType.LOADLEVEL, operation));                return operation;            }        }        private IEnumerator _loadLevel(string path, HandleFinishLoadLevel handle, ResourceType resourceType, ResourceAsyncOperation operation)        {            //使用assetbundle打包            if (UsedAssetBundle)            {                //加载场景assetbundle                     int scenAssetBundleSize = 0;                byte[] binary = ResourceCommon.getAssetBundleFileBytes(path, ref scenAssetBundleSize);                AssetBundle assetBundle = AssetBundle.LoadFromMemory(binary);                if (!assetBundle)                    Debug.LogError("create scene assetbundle " + path + "in _LoadImmediate failed");                //添加场景大小                operation.mLoadDependencesAssetSize += scenAssetBundleSize;                AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(ResourceCommon.getFileName(path, false));                operation.asyncOperation = asyncOperation;                yield return asyncOperation;                 handleResponse();                operation.asyncOperation = null;                operation.mComplete = true;                operation.mResource = null;                if (null != handle)                    handle();            }            //不使用           else            {                ResourceUnit level = new ResourceUnit(null, 0, null, path, resourceType);                //获取加载场景名称                string sceneName = ResourceCommon.getFileName(path, true);                AsyncOperation asyncOperation = Application.LoadLevelAsync(sceneName);                operation.asyncOperation = asyncOperation;                yield return asyncOperation;                handleResponse();                operation.asyncOperation = null;                operation.mComplete = true;                if (null != handle)                    handle();            }        }        //单个资源加载        ResourceUnit _LoadImmediate(string fileName, ResourceType resourceType)        {            //没有该资源,加载            if (!mLoadedResourceUnit.ContainsKey(fileName))            {                //资源大小                int assetBundleSize = 0;                byte[] binary = ResourceCommon.getAssetBundleFileBytes(fileName, ref assetBundleSize);                AssetBundle assetBundle = AssetBundle.LoadFromMemory(binary);                if (!assetBundle)                    Debug.LogError("create assetbundle " + fileName + "in _LoadImmediate failed");                Object asset = assetBundle.LoadAsset(fileName);                if (!asset)                    Debug.LogError("load assetbundle " + fileName + "in _LoadImmediate failed");                ResourceUnit ru = new ResourceUnit(assetBundle, assetBundleSize, asset, fileName, resourceType);                //添加到资源中                mLoadedResourceUnit.Add(fileName, ru);                return ru;            }            else            {                return mLoadedResourceUnit[fileName];            }        }}

    资源管理类进行到这里其实还没有完成,有的读者可能会说,UI 资源的处理,比如要把一个 UI 资源结点动态的挂接到父类的下面,该如何处理?这问题提的非常好,我们在资源管理框架中会专用于 UI 资源的类处理。代码实现如下:

        public class LoadUiResource{    public static GameObject LoadRes(Transform parent,string path)    {        if(CheckResInDic(path))                 {            if(GetResInDic(path) != null){                return GetResInDic(path);            }            else{                LoadResDic.Remove(path);            }        }        GameObject objLoad = null;        ResourceUnit objUnit = ResourcesManager.Instance.loadImmediate(path, ResourceType.PREFAB);        if (objUnit == null || objUnit.Asset == null)        {            Debug.LogError("load unit failed" + path);            return null;        }        objLoad = GameObject.Instantiate(objUnit.Asset) as GameObject;        objLoad.transform.parent = parent;        objLoad.transform.localScale = Vector3.one;        objLoad.transform.localPosition = Vector3.zero;        LoadResDic.Add(path,objLoad);        return objLoad;    }    //创建窗口子对象,不加入资源管理    public static GameObject AddChildObject(Transform parent, string path)    {        GameObject objLoad = null;        ResourceUnit objUnit = ResourcesManager.Instance.loadImmediate(path, ResourceType.PREFAB);        if (objUnit == null || objUnit.Asset == null)        {            Debug.LogError("load unit failed" + path);            return null;        }        objLoad = GameObject.Instantiate(objUnit.Asset) as GameObject;        objLoad.transform.parent = parent;        objLoad.transform.localScale = Vector3.one;        objLoad.transform.localPosition = Vector3.zero;        return objLoad;    }    //删除所有的孩子    public static void ClearAllChild(Transform transform)    {        while (transform.childCount > 0)        {            GameObject.DestroyImmediate(transform.GetChild(0).gameObject);        }        transform.DetachChildren();    }    public static void ClearOneChild(Transform transform,string name)    {        for (int i = 0; i < transform.childCount; i++)        {            if (transform.GetChild(i).gameObject.name == name)            {                GameObject.DestroyImmediate(transform.GetChild(i).gameObject);            }        }    }    //删除加载    public static void DestroyLoad(string path)    {        if(LoadResDic == null || LoadResDic.Count == 0)            return;        GameObject obj = null;        if (LoadResDic.TryGetValue(path, out obj) && obj != null)        {            GameObject.DestroyImmediate(obj);            LoadResDic.Remove(path);            //System.GC.Collect();        }    }    public static void DestroyLoad(GameObject obj)    {        if(LoadResDic == null || LoadResDic.Count == 0)            return;        if(obj == null)            return;        foreach(string key in LoadResDic.Keys)        {            GameObject objLoad;            if(LoadResDic.TryGetValue(key,out objLoad) && objLoad == obj)            {                GameObject.DestroyImmediate(obj);                LoadResDic.Remove(key);                break;            }        }           }    //获取在目录中的资源    public static GameObject GetResInDic(string path)    {        if(LoadResDic == null || LoadResDic.Count == 0)            return null;        GameObject obj = null ;        if(LoadResDic.TryGetValue(path,out obj))        {            return obj;        }        return null;    }    //检查资源是否存在    public  static bool CheckResInDic(string path)    {        if(LoadResDic == null || LoadResDic.Count == 0)            return false;        return LoadResDic.ContainsKey(path);    }    public static void Clean()    {        if(LoadResDic == null || LoadResDic.Count == 0)            return;        for(int i = LoadResDic.Count - 1;i >=0;i--)        {            GameObject obj = LoadResDic.ElementAt(i).Value ;            if( obj != null)            {                GameObject.DestroyImmediate(obj);            }        }        LoadResDic.Clear();    }    public static Dictionary<string,GameObject> LoadResDic = new Dictionary<string, GameObject>();}

    该类主要作用是提供了加载 UI 资源的接口,同时会将资源放到字典中便于统一处理。

    这样整个资源管理的设计就完成了,在使用时需要把 ResourceManager 类挂接到对象上,目的是为了同资源更新模块结合起来。

    第02课:自定义消息分发类模块

    为什么要使用消息分发函数?在 Unity 代码设计中,这个问题是不可回避的,因为在开发产品时,不可避免的是各个模块之间会有或多或少的联系,但是为了模块的扩展性,各个代码模块之间的耦合性必须降低,否则产品上线后,版本迭代会出现各种问题。有人可能会说,可以使用单例模式、静态类等等,在此就给读者普及一下知识点。

    先说一下单例模式,如果逻辑相对来说比较简单,它是可以的,但是如果逻辑比较复杂,那单例的调用会非常频繁,从而导致逻辑混乱,这是不可取的。静态类是常驻内存的,在游戏开发中除了一些指定的加载数据常驻内存,一般不会使用过多的静态类,所以也是不可取的。而且单例和静态二者也不会降低模块之间的耦合性,最终我们只能考虑消息分发函数,下面先介绍 Unity 引擎自带的消息分发函数。

    Unity 自带的消息分发函数

    Unity 引擎也为开发者提供了消息分发函数:SendMessage、SendMessageUpwards、BroadcastMessage,它们也可以实现简单的消息发送,函数内部的参数在这里就不一一介绍了。现在说一下为什么不选择它,因为它们的执行效率相对委托来说是比较低的,网上有关于测试效率的案例,而且扩展性方面也不好,比如我会使用很多的参数进行传递,它很难满足我们的需求,游戏开发还会有更多的类似需求。所以我们放弃它们,选择使用委托自己去封装。

    为什么自定义消息分发类模块

    自己定义消息分发,选择的也是委托的方式,首先我们要清楚封装事件是用于做啥事情的?先举一个需求说明。

    当玩家杀怪获取到掉落下来的道具时,玩家的经验值加1。这是一个很基础的功能需求,这类需求充斥着游戏的所有地方。当然我们可以不使用事件系统,直接在 OnTriggerEnter 方法中给该玩家的生命值加1就好了,但是,这将使得检测碰撞的这块代码直接引用了玩家属性管理的代码,也就是代码的紧耦合。而且,在后来的某一天,我们又想让接到道具的同时还在界面上显示一个图标,这时又需要在这里引用界面相关的代码。后来,又希望能播放一段音效……,这样随着需求的增加,逻辑会越来越复杂。解决此问题的好办法就是,在 OnTrigerEnter 中加入消息分发函数,这样具体的操作就在另一个类的函数中进行,耦合性降低。

    另外,在网络游戏中,我们也会遇到服务器发送给客户端角色信息后,客户端接收到该消息后,接下来做会将得到的角色信息在 UI 上显示出来。如果不用事件系统对其进行分离,那么网络消息跟 UI 就混在一起了。这样随着逻辑的需求增加,耦合性会越来越大,最后会导致项目很难维护。

    既然事件系统这么重要,我们必须要使用它解耦合模块,下面说说设计思路。

    事件系统的设计思想

    游戏中会有很多事件,事件的分类表示我们可以采用字符串或者采用枚举值,事件系统使用的是枚举值,事件分类枚举代码表示如下所示:

        public enum EGameEvent{    eWeaponDataChange = 1,    ePlayerShoot = 2,    //UI    eLevelChange = 3,    eBloodChange = 4,    ePowerChange = 5,    eSkillInit = 6,    eSkillUpdate = 7,    eBuffPick = 8,    eTalent = 9,    eBlood = 10,    eMp = 11,    eScore = 12,    ePower = 13,    eTalentUpdate = 14,    ePickBuff = 15,    eGameEvent_LockTarget,    //Login    eGameEvent_LoginSuccess,  //登陆成功    eGameEvent_LoginEnter, //登录界面    eGameEvent_LoginExit,    eGameEvent_RoleEnter,    eGameEvent_RoleExit,    //Play    eGameEvent_PlayEnter,    eGameEvent_PlayExit,    ePlayerInput,    eActorDead,}

    这些事件分类还可以继续扩展,事件系统贯穿于整个游戏,从 UI 界面、登录、战斗等等。我们的事件系统实现主要分为三步:事件监听、事件分发、事件移除。还有一个问题,事件和委托是保存在哪里的?我们使用了字典 Dictionary 用于保存事件和委托。代码如下:

        static public Dictionary<EGameEvent, Delegate> mEventTable = new Dictionary<EGameEvent, Delegate>(); 

    事件系统中的委托,也需要我们自己封装,可以思考一下,委托该如何封装?我们使用的委托函数的参数可能会有多个,而且不同的委托函数对应的类型可能也是不同的,比如 GameObject、float、int 等等。针对这些需求,唯一能帮我们解决问题的就是模版类,回调函数对应的代码如下:

    public delegate void Callback();public delegate void Callback<T>(T arg1);public delegate void Callback<T, U>(T arg1, U arg2);public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);public delegate void Callback<T, U, V, X>(T arg1, U arg2, V arg3, X arg4);

    最多列举了四个参数的回调函数,下面开始事件类的封装了。先封装监听函数:

        //无参数    static public void AddListener(EGameEvent eventType, Callback handler) {        OnListenerAdding(eventType, handler);        mEventTable[eventType] = (Callback)mEventTable[eventType] + handler;    }    //一个参数    static public void AddListener<T>(EGameEvent eventType, Callback<T> handler) {        OnListenerAdding(eventType, handler);        mEventTable[eventType] = (Callback<T>)mEventTable[eventType] + handler;    }    //两个参数    static public void AddListener<T, U>(EGameEvent eventType, Callback<T, U> handler) {        OnListenerAdding(eventType, handler);        mEventTable[eventType] = (Callback<T, U>)mEventTable[eventType] + handler;    }    //三个参数    static public void AddListener<T, U, V>(EGameEvent eventType, Callback<T, U, V> handler) {        OnListenerAdding(eventType, handler);        mEventTable[eventType] = (Callback<T, U, V>)mEventTable[eventType] + handler;    }    //四个参数    static public void AddListener<T, U, V, X>(EGameEvent eventType, Callback<T, U, V, X> handler) {        OnListenerAdding(eventType, handler);        mEventTable[eventType] = (Callback<T, U, V, X>)mEventTable[eventType] + handler;    }

    每个函数都比较简单,从没有参数,到最多四个参数的函数一一给读者展示出来。这些函数都调用了函数 OnListenerAdding 用于将事件和委托粗放到字典中,监听函数有了,对应的就是移除监听函数,移除就是从 Dictionary 字典中将其移除掉,它跟监听函数是一一对应的函数如下:

        //No parameters    static public void RemoveListener(EGameEvent eventType, Callback handler) {        OnListenerRemoving(eventType, handler);           mEventTable[eventType] = (Callback)mEventTable[eventType] - handler;        OnListenerRemoved(eventType);    }    //Single parameter    static public void RemoveListener<T>(EGameEvent eventType, Callback<T> handler) {        OnListenerRemoving(eventType, handler);        mEventTable[eventType] = (Callback<T>)mEventTable[eventType] - handler;        OnListenerRemoved(eventType);    }    //Two parameters    static public void RemoveListener<T, U>(EGameEvent eventType, Callback<T, U> handler) {        OnListenerRemoving(eventType, handler);        mEventTable[eventType] = (Callback<T, U>)mEventTable[eventType] - handler;        OnListenerRemoved(eventType);    }    //Three parameters    static public void RemoveListener<T, U, V>(EGameEvent eventType, Callback<T, U, V> handler) {        OnListenerRemoving(eventType, handler);        mEventTable[eventType] = (Callback<T, U, V>)mEventTable[eventType] - handler;        OnListenerRemoved(eventType);    }    //Four parameters    static public void RemoveListener<T, U, V, X>(EGameEvent eventType, Callback<T, U, V, X> handler) {        OnListenerRemoving(eventType, handler);        mEventTable[eventType] = (Callback<T, U, V, X>)mEventTable[eventType] - handler;        OnListenerRemoved(eventType);    }    

    监听函数和移除监听函数都封装完了,那么如何触发监听函数这就是我们通常所说的广播函数,它与监听和移除也是一一对应的,代码片段如下所示:

        //No parameters    static public void Broadcast(EGameEvent eventType) {        OnBroadcasting(eventType);        Delegate d;        if (mEventTable.TryGetValue(eventType, out d)) {            Callback callback = d as Callback;            if (callback != null) {                callback();            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    }    //Single parameter    static public void Broadcast<T>(EGameEvent eventType, T arg1) {        OnBroadcasting(eventType);        Delegate d;        if (mEventTable.TryGetValue(eventType, out d)) {            Callback<T> callback = d as Callback<T>;            if (callback != null) {                callback(arg1);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    }    //Two parameters    static public void Broadcast<T, U>(EGameEvent eventType, T arg1, U arg2) {        OnBroadcasting(eventType);        Delegate d;        if (mEventTable.TryGetValue(eventType, out d)) {            Callback<T, U> callback = d as Callback<T, U>;            if (callback != null) {                callback(arg1, arg2);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    }    //Three parameters    static public void Broadcast<T, U, V>(EGameEvent eventType, T arg1, U arg2, V arg3) {        OnBroadcasting(eventType);        Delegate d;        if (mEventTable.TryGetValue(eventType, out d)) {            Callback<T, U, V> callback = d as Callback<T, U, V>;            if (callback != null) {                callback(arg1, arg2, arg3);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    }    //Four parameters    static public void Broadcast<T, U, V, X>(EGameEvent eventType, T arg1, U arg2, V arg3, X arg4) {        OnBroadcasting(eventType);        Delegate d;        if (mEventTable.TryGetValue(eventType, out d)) {            Callback<T, U, V, X> callback = d as Callback<T, U, V, X>;            if (callback != null) {                callback(arg1, arg2, arg3, arg4);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    }    }

    另外把 OnListenerAdding 函数封装如下,它主要是将事件和委托存放到字典中,如下所示:

        static public void OnListenerAdding(EGameEvent eventType, Delegate listenerBeingAdded) {    if (!mEventTable.ContainsKey(eventType)) {        mEventTable.Add(eventType, null );    }    Delegate d = mEventTable[eventType];    if (d != null && d.GetType() != listenerBeingAdded.GetType()) {        throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));    }}

    这样我们的整个事件系统就封装完成了,最后告诉读者如何使用?首先需要先监听,将监听函数放在对应的类中,代码如下所示:

    EventCenter.AddListener(EGameEvent.eGameEvent_GamePlayEnter, Show);

    然后在另一个类文件中,可以播放此消息。代码如下所示:

    EventCenter.Broadcast(EGameEvent.eGameEvent_GamePlayEnter);
    第03课:游戏对象池设计
    第04课:UI 架构设计
    第05课:UI 架构案例实现
    第06课:角色系统设计(上)
    第07课:角色系统设计(下)
    第08课:技能系统设计
    第09课:游戏文件加载读取
    第10课:PhotonServer 服务器部署
    第11课:角色同步解决方案
    第12课:网络游戏案例讲解

    阅读全文: http://gitbook.cn/gitchat/column/5a3921aec5896e6e1cf1a129

    展开全文
  • 传统行业发展了很多年,有一套成熟的理论,而软件设计这个行业才几十年,在实践中,为了提高生产效率和品质,工程化是一个必然化的趋势,于是传统行业工程化的理论和实践就有了在软件设计这个行业移植的可能性。...
  • 个案研究以C&E矩阵进行专业选择--六标准差的专案架构(1).pptx
  • 软件架构五大原则

    2020-08-04 09:20:09
    方案架构师是负责系统架构以及特定产品的技术标准(包括技术、平台、基础架构)的专家。他们为产品设定前景,他们的分析也是产品的定义、设计、交付和永久支持的成功关键。因此,构架师不仅需要了解业务需求,还需要...
  • 线框向整个团队展示了布局、导航、视觉层次、信息架构和内容优先级等关键的问题,它勾勒出了整个项目的基本轮廓,呈现了关键信息。而今天的文章会帮你了解提升线框设计品质的所需要知道的一切。继续读下去,你会...
  • 格科微(Galaxycore)电子(上海)有限公司推出1/4英寸VGA图像传感器,具有尺寸小,功耗低,图像品质好,成本比较低等特点,能够广泛用于手机、PDA、电脑摄像头、玩具等消费类电子领域。 作为大陆地区涉足CMOS IMAGE ...
  • 图解物联网---物联网的架构

    千次阅读 2021-03-10 00:44:55
    物联网架构1、物联网的作用实现物联网时,物联网服务大体上发挥着两个作用。第一是把从设备收到的数据保存到数据库,并对采集的数据进行分析。第二是向设备发送指令和信息。本章将会为大家介绍如何构建...
  • 软件架构设计-软件架构风格、分层架构

    万次阅读 多人点赞 2021-06-12 15:39:23
    一、软件架构设计 软件或计算机系统的软件架构是该系统的一个(或多个)结构,而结构由软件元素、元素的外部可见属性及它们之间的关系组成。 软件系统架构是关于软件系统的 结构、行为和属性 的高级抽象。指定了软件...
  • 一文读懂数字政府及其业务架构

    千次阅读 2021-03-17 00:58:04
    业务架构图如下所示: 营商环境域:优化市级办事大厅、功能区级政务服务中心、镇街级综合服务中心和村(社区)党群服务中心政务服务资源配置,创新政务服务供给方式,基于一体化政务服务平台,建设企业身边的综合...
  • GPU架构与桌面级图形显卡

    千次阅读 2022-03-31 14:27:57
    GPU架构与桌面级图形显卡 GPU芯片 设计为主的集成电路高科技公司。致力于创新面向元计算应用的新一代GPU,构建融合视觉计算、3D图形计算、科学计算及人工智能计算的综合计算平台,建立基于云原生GPU计算的生态系统,...
  • Android camera架构

    2020-05-31 21:43:04
    可以说是架构上变动最大最频繁的子系统。很多设备仍然依赖相机 HAL1,因此 Android 7.0 继续支持该模块。此外,Android 相机服务还支持同时实现两种 HAL(1 和 3),如果您希望通过相机 HAL1 支持性能
  • 格科微(Galaxycore)电子(上海)有限公司推出1/4英寸VGA图像传感器,具有尺寸小,功耗低,图像品质好,成本比较低等特点,能够广泛用于手机、PDA、电脑摄像头、玩具等消费类电子领域。 作为大陆地区首家涉足CMOS IMAGE...
  • 阿里NLP架构与应用

    万次阅读 2017-11-24 15:42:01
    NLP技术的应用及思考 背景介绍 阿里巴巴的生态系统下面有很多的...所以对于阿里巴巴而言,这个可以非常简练的概括我们在做什么,中间是最重要的数据,下面数据包含了最核心的也是阿里巴巴最早起家的来自于电商的
  • YCT产品研发部组织结构及岗位职责 一, 组织结构 互联网其他公司组织结构: 本公司的产品研发部组织结构: 二, 岗位职责,岗位要求以及社企对应薪资 产品(交互)设计: ...2、确定产品的...
  • 关于游戏架构设计(一)

    千次阅读 2019-02-21 23:05:56
    游戏中的资源量是必须要考虑的问题,游戏品质的好坏都是通过资源表现的,这些资源的管理,作为开发者必须要处理的。对于游戏资源管理,通常的做法是简单的封装几个接口用于资源的加载,如果只是做个 Demo,这样做是...
  • SD-WAN三种基础技术架构

    千次阅读 2021-07-26 11:27:17
    所示: 简单介绍一下这种架构: 不论是传统硬件厂商、VPN厂商或新型的SD-WAN公司,设计一套SD-WAN控制器,部署在云端或总部,基于互联网统一管理分支机构的CPE设备并组网: 在本地分支机构部署SD-WAN的CPE,...
  • 我对架构设计的理解

    千次阅读 2019-02-09 13:53:52
    游戏架构设计是一个老生长谈的话题,以前给多个游戏公司培训过,随着时间的积累,对游戏架构设计的理解又多了一些,在此给读者分享一下我对于架构设计的理解。 游戏架构设计是基于引擎的基础上的二次封装,目的是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,109
精华内容 7,643
关键字:

品质架构图