盛泽网站建设,派代网,网站不备案可以做淘宝客吗,百度seo咋做您是否听说过#xff1a;“我们非常喜欢您的产品……除了一些小细节”。 然后#xff0c;CIO推出了一系列其他“必备”要求的清单#xff0c;其中有数百个要求添加到您的惊人产品中。 您是否听说过#xff0c;甚至说过#xff1a;“团队#xff0c;我们即将签署一份利润丰… 您是否听说过“我们非常喜欢您的产品……除了一些小细节”。 然后CIO推出了一系列其他“必备”要求的清单其中有数百个要求添加到您的惊人产品中。 您是否听说过甚至说过“团队我们即将签署一份利润丰厚的合同但是……” 然后客户对附加功能的愿望清单使开发人员感到头疼。 那么如何使产品远离客户的潜在危险想法同时又使他们满意呢 对于专门设计为以特定方式运行但现在具有大量附加组件的产品如何保持最高性能水平呢 为已开发的解决方案提供不间断且出色的支持的基本需求将带来多少挑战 在商业世界中产品定制已成为越来越令人期望的要求并且响应于这种客户需求已经发展了许多通用实践。 您可以在下面找到典型方法的概述。 如果您已经熟悉它们那么欢迎您直接滚动至“ 扩展方法 ”部分并了解我们如何以我们认为更有效的方式解决这些挑战。 一体 定制的最直接最明显的解决方案是实施一个核心产品所需的所有内容然后采用“ 功能切换 ”技术来满足每个特定客户的需求。 多合一方法的主要优点是保留了整体产品这对于某些类型的产品似乎是一种很好的方式这些产品通常可以满足业务需求而无需进行广泛的定制。 这种方法的自然局限性隐藏在“不需要太多定制”的假设中。 通常产品开发就是从这种信念开始的但是经过多次交付您才真正意识到需要多少特定于客户的功能。 陷入困境的情况并不少见。 拒绝定制开发并可能失去客户或者将源代码转换为具有针对单个客户的功能的垃圾箱 这些功能对于大多数最终用户而言可能是无用的。 您会选择哪个选项 显然在艰难和艰难的地方之间进行选择并不是成功的方法。 简介仅当您确定需要罕见且有限的定制时“多合一”方法才是合适的选择。 否则您将面临可管理和可支持产品与客户满意度之间的选择。 让我引用杰里·加西亚Jerry Garcia的话“不断选择两种邪恶中的较小者仍然是选择邪恶”。 分枝 如果重大定制是交付的“必不可少”部分则不能采用“多合一”技术。 还有另一种简单的方法- 分支 。 您只需分支产品代码库然后单独进行更改即可。 将分支与“多合一”进行比较最大的优点是没有适用于自定义范围的限制。 您使用单独的分支来满足不同客户的特定要求并避免在同一代码库中混合使用所有功能。 但是它的另一面可能会在产品发展方面变成死胡同。 显然产品分支是主要的开发空间大多数错误修正改进和新功能都首先被应用到产品中。 因此需要频繁合并以使所有定制分支与核心产品保持同步。 只要原始产品源代码不受定制分支的影响合并是一项简单的操作否则合并将变得非常耗时并可能导致不可避免的回归错误。 如果您仅限于很少的自定义分支则此方法仍然可以使用。 但是随着交付实例数量的增加面临“通过合并实施酷刑”的可能性迫在眉睫。 简介 分支方法无疑是非常灵活和直接的-产品的任何部分都可以修改。 但是交付后阶段可能非常费力随着时间的推移变得更加困难并且不太可能导致交付大量可管理的定制分支。 实体-属性-价值模型 实体-属性-价值模型 又名对象-属性-价值模型垂直数据库模型和开放式架构是众所周知的且被广泛使用的数据模型。 EAV支持动态实体属性通常与标准关系模型并行使用。 从产品化的角度来看使用EAV的主要优点是您可以“按原样”交付产品然后通过在运行时添加所需的属性来调整数据模型从而保持源代码的整洁。 与以往一样还有一个缺点 有限的适用性–仅通过允许向实体添加属性来限制EAV模型然后根据预编程的逻辑将其自动嵌入到UI中。 额外的数据库服务器负载–垂直数据库设计通常成为企业应用程序的瓶颈企业应用程序通常使用大量与它们相关的实体和属性进行操作。 最后如果没有复杂的报告引擎就无法想象企业系统。 EAV模型具有“垂直”数据库结构因此有可能带来许多麻烦。 简介 实体-属性-价值模型在某些情况下具有很大的价值例如当需要提供通过具有附加信息性数据而实现的灵活性时该信息性数据未在业务逻辑中明确使用。 换句话说EAV具有良好的适度性例如除了标准的关系模型和插件体系结构之外 。 插件架构 插件体系结构是最流行且功能最强大的方法之一其中功能逻辑作为单独的工件称为插件保存。 要覆盖现有的开箱即用行为并运行插件必须在产品源代码中定义“定制点”即扩展点。 “定制点”是源代码中的某个位置应用程序在该位置上浏览附加的插件以检查插件是否包含要在此处运行的替代实现。 插件体系结构的一种变化是外部脚本。 在实现功能实现并将其作为脚本存储在外部时。 脚本调用也由预定义的“定制点”控制。 使用这种插件方法可以使产品“清洁”特定的客户要求“按原样”交付核心产品并根据插件或脚本的要求自定义行为。 这种方法的另一个优点是管理完善的更新过程。 产品和插件功能的完全分离使彼此之间可以独立更新。 当然存在限制主要限制是不可能完全知道将来可能会提出哪些自定义要求。 因此只能猜测应该在何处嵌入“定制点”。 当然这些可以作为缓解“防万一”计划的零星散布在各处但这将导致代码可读性差硬调试以及复杂的支持。 简介如果易于预测“定制点”那么插件架构确实可以工作但是请注意“定制点”之间的定制是不可能的。 扩展方法 我们在企业软件开发平台CUBA中实施了独特的方法。 正如我们上一篇文章所述 CUBA是一种非常实用的活生物体它是通过开发人员驱动的演变过程创建的。 因此根据我们在现成产品上的丰富经验我们提出了两个最终要求 客户特定的代码应与核心产品代码完全分开 产品代码的每个部分都应该可以修改 我们设法满足了这些要求并通过我们的“扩展”机制实现了更多目标。 CUBA扩展 扩展是一个单独的CUBA项目它继承了基础项目即您的核心产品的所有功能并将其用作库。 显然这使开发人员可以实现全新的功能而不会影响父项目但是由于使用了“ 开放继承”模式和特殊的CUBA工具您还可以覆盖父项目的任何部分。 总之扩展是实现本文开头讨论的数百个“少量次要细节”的地方。 实际上每个CUBA项目都是CUBA平台本身的扩展-因此它可以覆盖任何平台功能。 我们自己采用了这种方法以从核心平台中分离出一些现成的功能全文搜索报告图表等。 因此如果您在项目中需要它们则只需将它们添加为父项目-就是这样是多重继承 您可以用相同的方式构建分层的定制模型 。 这听上去很复杂但是很合理。 让我举一个真实的例子 Sherlock –是Haulmont完整的出租车管理解决方案支持从预定派遣到应用程序和计费的出租车业务的方方面面。 该解决方案涵盖了客户业务的许多不同方面其中许多是与位置相关的。 例如所有英国出租车公司都具有相同的法律法规但是其中许多法规不适用于美国反之亦然。 显然我们不想在核心产品中实施所有这些规定因为 这是“特定于操作区域”的功能 当地法规可能会对不同国家的出租车队运营产生完全不同的影响 一些客户根本不需要监管控制 因此我们组织了多级扩展层次结构 核心产品包含出租车业务的通用功能 定制的第一层实现了区域特性 第二层定制涵盖了客户的愿望清单如果有的话 干净利落。 如您所见通过使用扩展您既不需要分支也不需要在核心产品中集成所有需求 因此代码保持简洁且易于管理。 听起来真是太好了所以让我们看看它是如何工作的 向现有实体添加新属性 假定我们具有用户实体的产品定义该产品定义由两个字段组成登录名和密码 Entity(name product$User)
Table(name PRODUCT_USER)
public class User extends StandardEntity {Column(name LOGIN)protected String login;Column(name PASSWORD)protected String password;//getters and setters
} 现在我们的一些客户提出了一项附加要求即向用户添加“家庭住址”字段。 为此我们在扩展中扩展User实体 Entity(name ext$User)
Extends(User.class)
public class ExtUser extends User {Column(name ADDRESS, length 100)private String address;public String getAddress() {return address;}public void setAddress(String address) {this.address address;}
} 您可能已经注意到除Extends以外的所有注释都是通用的JPA注释。 Extends属性是CUBA引擎的一部分它甚至在整个产品功能上都将User实体全局替换为ExtUser 。 使用Extends属性我们强制平台执行以下操作 始终创建“最新子级”类型的实体 User user metadata.create(User.class); //ExtUser entity will be created 在执行之前转换所有JPQL查询以便它们始终返回“最新子集” select u from product$User u where u.name :name //returns a list of ExtUsers 始终在关联实体中使用“最新子项” userSession.getUser(); //returns an instance of ExtUser type 换句话说如果声明了扩展实体则基础实体将在整个解决方案产品和扩展中被放弃并且被扩展实体全局覆盖。 屏幕定制 因此我们通过添加地址属性扩展了User实体现在希望更改能够反映在用户界面中。 首先让我们看一下原始产品屏幕声明 windowdatasourceuserDscaptionmsg://captionclasscom.haulmont.cuba.gui.app.security.user.edit.UserEditormessagesPackcom.haulmont.cuba.gui.app.security.user.editdsContextdatasourceiduserDsclasscom.haulmont.cuba.security.entity.Userviewuser.edit/datasource/dsContextlayoutfieldGroup idfieldGroup datasourceuserDscolumnfield idlogin/field idpassword//column/fieldGroupiframe idwindowActions screeneditWindowActions//layout/window 如您所见CUBA屏幕描述符表示为普通XML。 显然我们可以简单地在扩展名中重新声明整个屏幕描述符但这意味着要复制粘贴其中的大部分内容。 因此如果将来产品屏幕中发生某些更改我们将必须手动将这些更改复制到扩展屏幕中。 为避免这种情况CUBA引入了屏幕继承机制您所需要的只是描述对屏幕的更改 window extends/com/haulmont/cuba/gui/app/security/user/edit/user-edit.xmllayoutfieldGroup idfieldGroupcolumnfield idaddress//column/fieldGroup/layout/window 您可以使用extends属性定义祖先屏幕并且仅描述要更改的主题。 这个给你 最后让我们看一下结果 修改业务逻辑 为了实现业务逻辑修改CUBA平台使用Spring框架该框架构成了平台基础结构的核心部分。 例如您有一个中间件组件来执行价格计算过程 ManagedBean(product_PriceCalculator)
public class PriceCalculator {public void BigDecimal calculatePrice() { //price calculation}
} 要覆盖价格计算实现我们只需要执行两个简单的操作。 首先扩展产品类并覆盖相应的过程 public class ExtPriceCalculator extends PriceCalcuator {Overridepublic void BigDecimal calculatePrice() { //modified logic goes here}
} 最后使用产品Bean标识符在Spring配置中注册新类 bean idproduct_PriceCalculator classcom.sample.extension.core.ExtPriceCalculator/ 现在 PriceCalculator注入将始终返回扩展类实例。 因此修改后的实现将在整个产品中使用。 扩展基础产品版本 随着核心产品的发展和新版本的发布您最终将决定将扩展程序升级到最新的产品版本。 过程非常简单 在扩展中指定基础产品的新版本。 重建扩展名 如果扩展是基于产品API的稳定部分构建的则可以运行它。 如果对产品API进行了一些重大修改并且这些修改与扩展中实现的自定义重叠则有必要在扩展中支持新产品API。 大多数情况下产品API在每次更新之间都不会发生重大变化尤其是在次要版本中。 但是即使API发生了“大爆炸”产品通常也至少保持几个未来版本的向下兼容性并且旧的实现被标记为“已弃用”从而允许将所有扩展迁移到最新的API。 结论 作为一个简短的摘要我想以表格形式说明比较分析的结果 一体 分枝 电动汽车 外挂程式 CUBA扩展 独立于架构 – – – 动态定制 – – /- – 业务逻辑定制 – /- 数据模型定制 /- 用户界面定制 /- /- 代码质量和可读性 – /- /- /- 不影响性能 – 软件回归的风险 高 高 低 介质 介质 长期支持的复杂性 极端的 极端的 低 介质 介质 可扩展性 低 低 高 高 高 如您所见扩展方法功能强大但是它缺少的一件事就是能够动态地微调系统动态定制。 为了克服这个问题CUBA还提供了对实体属性值模型和插件/脚本方法的全面支持。 我希望您会发现此概述很有用当然您的反馈意见也将受到赞赏。 翻译自: https://www.javacodegeeks.com/2015/07/how-to-develop-a-highly-customizable-product.html