我的BO

1-我的BO之强类型

2-我的BO之数据保护

3-我的BO之状态控制

4-我的BO之导航属性

数据保护指什么

软件的运行离不开数据,数据一般存在对象中。这种对象在 Java 统称为 POJO,在 C# 则为 POCO。若 POJO 的Property(属性)都是可读写的(publicget/set),没有方法或只有少量的持久化方法,这种称为贫血模型。

贫血模型只存储数据而对数据没有控制,对象内部和外部都能修改。业务逻辑一般写在外部,就算写在内部也因为 Property 可由外界随意修改而控制不住数据。从而这个 POJO 只是存储数据,没有控制数据的能力,也就没有保护数据的能力。一个对象相关的逻辑不能被很好地集中管理,而是分散在各个外部方法中。从而产生了几个不良后果:

贫血模型缺点多

  1. 不同情况下对同份数据的处理有逻辑矛盾,且不易发现。如一处设属性值为-1,另一处却没有考虑值是-1的情况。
  2. 相同的功能写多份,修改时容易改漏。经常复制粘贴的代码后果很可怕。
  3. 软件的质量很差,容易发生改了一个 Bug 产生两个 Bug。难以交付。

BO 如何保护数据

BO 中全部的 Property 对外都是只读的,外部无法直接修改 Property。只有 BO 自己内部才能修改属性,从而保护了数据。外部通过调用 BO 的业务方法修改一个或多个属性,业务方法可对参数和状态进行各种判断,满足条件才修改数据。从而保证任何时候 BO 的数据都是合法的。由于数据只能在内部修改,所以什么值代表什么意思是由 BO 自己决定的,统一决定,而不是由外部分散在各个地方的代码逻辑决定的,所以有效地保证了数据含义的统一性。

虽然关于这个 BO 自己的业务都写到 BO 这个类中了(这里暂无须考虑通过继承写到多个类的情况),起到了集中控制的效果。但内部不同的业务方法也可能有共用的逻辑,这些应该通过重构功能,以达到“一个功能只写一处”的状态。一个大功能会包含若干个小功能,这里的“功能”,是泛指大大小小的任何一个功能。无论是面向对象还是面向过程,都应该努力做到“一个功能只写一处”。这是解决软件灵活性与软件正确性的唯一比较可取的做法。除非把人当作机器,面对之前大量“复制粘贴再改改”的代码需要修改时也能毫不遗漏地全部修改。

BO 的实例化

BO 被定义为任何时刻都是有意义的,所以并不能new一个空对象,然后再赋值,数据必须在new时就送来,所以 BO 需要提供一个全部属性的构造方法。另外为了持久化方便的考虑,也可以接收一个 PO,从 PO 获取各个属性的值。

网上购物的订单示例

public class OrderBo extends BoBase {
Order order;
@Autowired
OrderDB orderDB; public OrderBo(Order order) { /* 传入 PO */
if (order == null)
throw ParameterException.missData("order");
this.order = order;
} public OrderBo(Long buyerId, Long postDistrictId, String postAddress, String postContactPhone, String evaluation,
Date createTime, OrderStatus status, EvaluateStatus evaluateStatus) { /* 传入各个 Property */
Order entity = new Order();
entity.setBuyerId(buyerId);
entity.setPostDistrictId(postDistrictId);
entity.setPostAddress(postAddress);
entity.setPostContactPhone(postContactPhone);
entity.setEvaluation(evaluation);
entity.setCreateTime(createTime);
entity.setStatus(status.toString());
entity.setEvaluateStatus(evaluateStatus);
this.order = entity;
this.trackState = BoTrackState.Added;
} // region 属性 public Long getId() {
return order.getId();
} protected void setId(Long id) { /* 每个 set 都不是 public */
order.setId(id);
setTrackUpdate(); /* 父类方法,后续文章会介绍 */
}
// 买家 Id
public Long getBuyerId() {
return order.getBuyerId();
} protected void setBuyerId(Long buyerId) {
order.setBuyerId(buyerId);
setTrackUpdate();
}
// 区县 Id
public Long getPostDistrictId() {
return order.getPostDistrictId();
} protected void setPostDistrictId(Long postDistrictId) {
order.setPostDistrictId(postDistrictId);
setTrackUpdate();
}
// 详细地址
public String getPostAddress() {
return order.getPostAddress();
} protected void setPostAddress(String postAddress) {
order.setPostAddress(postAddress);
setTrackUpdate();
}
// 联系手机
public String getPostContactPhone() {
return order.getPostContactPhone();
} protected void setPostContactPhone(String postContactPhone) {
order.setPostContactPhone(postContactPhone);
setTrackUpdate();
}
// 评价内容
public String getEvaluation() {
return order.getEvaluation();
} protected void setEvaluation(String evaluation) {
order.setEvaluation(evaluation);
setTrackUpdate();
}
// 下单时间,创建时间
public Date getCreateTime() {
return order.getCreateTime();
} protected void setCreateTime(Date createTime) {
order.setCreateTime(createTime);
setTrackUpdate();
}
// 订单状态
public OrderStatus getStatus() {
String sStatus = order.getStatus();
return OrderStatus.valueOf(sStatus);
} protected void setStatus(OrderStatus status) {
String sStatus = status.toString();
order.setStatus(sStatus);
setTrackUpdate();
}
// 评价状态:未评价,已评价
public EvaluateStatus getEvaluateStatus() {
String sStatus = order.getEvaluateStatus();
return EvaluateStatus.valueOf(sStatus);
} protected void setEvaluateStatus(EvaluateStatus evaluateStatus) {
String sStatus = evaluateStatus.toString();
order.setEvaluateStatus(sStatus);
setTrackUpdate();
}
// endregion 属性 // region 操作 public void delete() {
this.setTrackDeleted();
this.save();
} // 评价
public void evaluat(string evaluation) {
/* 不是特定的状态不能评价。若有需要,可以阻止内容为(空)的评价 */
if (this.getEvaluateStatus() != EvaluateStatus.未评价) {
throw ParameterException.invalidStatus("订单" + this.getEvaluateStatus() + ",不能评价");
} /* 修改评价内容,并修改评价状态,而不是内容和状态可以被外部分别调用! */
this.setEvaluation(evaluation);
this.setEvaluateStatus(EvaluateStatus.已评价);
this.save(); /* 父类方法,后续文章会介绍 */
}
// endregion 操作
}

/* */的内容是本文特意加的说明。

不足之处

没有对构造时的数据进行合法性验证。解决办法可以在构造时检查,若非法则抛异常。也可以私有化构造方法,并提供public static方法,若非法则返回null

感谢

感谢 Rayman(QQ:25625607) 在我为【BO 的属性该全只读,通过方法来修改】还是【可以部分属性可写(public set),并在写时作业务逻辑】举棋不定时给我了肯定的回答。我认为这是整个 BO 的关键所在。

系列导航

1-我的BO之强类型

2-我的BO之数据保护

3-我的BO之状态控制

4-我的BO之导航属性

我的BO之数据保护的更多相关文章

  1. 我的BO之导航属性

    我的BO 1-我的BO之强类型 2-我的BO之数据保护 3-我的BO之状态控制 4-我的BO之导航属性 数据需要导航 数据之间普遍存在关系,做业务处理时往往也是按照关系在数据之间查询和处理.业务处理可 ...

  2. 我的BO之状态控制

    我的BO 1-我的BO之强类型 2-我的BO之数据保护 3-我的BO之状态控制 4-我的BO之导航属性 MIS常有状态 信息管理系统(MIS)常常有流程,一个流程由多个环节构成,不同的环节的流转通过状 ...

  3. HDU 5754 Life Winner Bo 组合博弈

    Life Winner Bo Problem Description   Bo is a "Life Winner".He likes playing chessboard gam ...

  4. 【BO】SAP BO相关问题汇总贴

    本文将以往写过的关于SAP BO相关问题的帖子汇总了一下,方便寻找 #1 为WEBI报表添加自定义字体font #2 WEBI文件打开时提示Illegal access错误 #3 安装BO服务器时,o ...

  5. 【SAP BO】无法识别账户信息:无法访问CMS。计算机上的CMS由于某个严重错误而停止。(FWM 20031)

    1.系统环境 OS:Windows Server 2008 R2 RDBMS:Oracle 11g R2(Server.Client同时存在) BI:SAP Business Objects 4.2 ...

  6. PO,VO,BO,DTO,POJO(POCO),DAO的区别(转载)

    PO:persistant object持久对象 最形象的理解就是一个PO就是数据库中的一条记录.好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象. BO:business object业 ...

  7. ASP.NET Core 数据保护(Data Protection)【中】

    前言 上篇主要是对 ASP.NET Core 的 Data Protection 做了一个简单的介绍,本篇主要是介绍一下API及使用方法. API 接口 ASP.NET Core Data Prote ...

  8. ASP.NET Core 数据保护(Data Protection)【上】

    前言 上一篇博客记录了如何在 Kestrel 中使用 HTTPS(SSL), 也是我们目前项目中实际使用到的. 数据安全往往是开发人员很容易忽略的一个部分,包括我自己.近两年业内也出现了很多因为安全问 ...

  9. 【BO】为WEBI报表添加自定义字体font

    本篇主要讲解如何为sap business objects 的web intelligence报表组件新增字体.因为系统默认预设的字体对中文而言实在是太丑了,有的字体特喵的直接把中文变成方框框了! 一 ...

随机推荐

  1. SDL 实现多线程 的一些BUG

    1. SDL_init()  在多个线程初始化的时候  , 在第二个线程出现SDL_init 崩溃的现象  SDL init  错误码:0XFFFFFFFF 2. SDL_init() 如果只初始化一 ...

  2. jQuery遍历(2)

    上期我们讲了遍历的祖先和后代的问题,现在我们讲讲遍历同胞 同胞拥有相同的父元素. 通过 jQuery,您能够在 DOM 树中遍历元素的同胞元素. jQuery siblings() 方法 siblin ...

  3. vue使用技巧:Promise + async + await 解决组件间串行编程问题

    业务场景描述 大家都通过互联网投递过简历,比如在智联.58.猎聘等平台.投递心仪的职位前一般都需要前提创建一份简历,简历编辑界面常规的布局最上面是用户的个人基本信息,如姓名.性别.年龄.名族等,接着是 ...

  4. Node.js 实战(一)之—优化汇总

    Express 页面缓存 app.set("cache view",true); --设置页面缓存 开发模式下博主建议不要这么做,因为开发中我们会频繁的对页面的样式.js等进行修改 ...

  5. JDK反编译的两种方式

    环境 链接:https://pan.baidu.com/s/1DwWj5Kt4Gfi68k_EOAea_Q 提取码:57j2 apktools+dex2jar+gd-gui 方式一: apktools ...

  6. K2 BPM_当K2遇上医药,用流程打通企业的任督二脉_业务流程管理系统

    据调查,如今仍有60%的医药企业,存在合规经营和利润下降的困扰,在“研”.“产”.“供”.“销”的运营过程中,时时伴随着严苛的管理政策和法规.如何加强企业跨部门.跨组织.跨业务线的执行能力,始终是管理 ...

  7. OpenWrt增加软件包

    变量名含义 SECTION //包的种类 CATEGORY //显示在menuconfig的哪个目录下 SUBMENU //menuconfig子目录 TITLE //简单的介绍 DESCRIPTIO ...

  8. [LeetCode] 392. 判断子序列 ☆(动态规划)

    https://leetcode-cn.com/problems/is-subsequence/solution/java-dp-by-zxy0917-5/ 描述 给定字符串 s 和 t ,判断 s ...

  9. django_rest framework 接口开发(一)

    1 restful 规范(建议) 基于FbV def order(request): if request.method=="GET": return HttpResponse(' ...

  10. django内置缓存

    由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5 ...