研磨设计模式 之 代理模式(Proxy)1——跟着cc学设计系列

11.1  场景问题

11.1.1  访问多条数据

       考虑这样一个实际应用:要一次性访问多条数据。

       这个功能的背景是这样的;在一个HR(人力资源)应用项目中客户提出,当选择一个部门或是分公司的时候,要把这个部门或者分公司下的所有员工都显示出来,而且不要翻页,好方便他们进行业务处理。在显示全部员工的时候,只需要显示名称即可,但是也需要提供如下的功能:在必要的时候可以选择并查看某位员工的详细信息。

       客户方是一个集团公司,有些部门或者分公司可能有好几百人,不让翻页,也就是要求一次性的获取这多条数据并展示出来。

       该怎么样实现呢?

11.1.2  不用模式的解决方案

       不就是要获取某个部门或者某个分公司下的所有员工的信息吗?直接使用sql语句从数据库中查询就可以得到,示意性的SQL大致如下:

String sql = "select * from 用户表,部门表 "

       +"where 用户表.depId=部门表.depId "

       +"and 部门表.depId like '"+用户选择查看的depId+"%'";

       为了方便获取某个部门或者某个分公司下的所有员工的信息,设计部门编号的时候,是按照层级来进行编码的,比如:上一级部门的编码为“01”,那么本级的编码就是“0101”、“0102”……以此类推,下一级的编码就是“010101”、“010102”……。

       这种设计方式,从设计上看虽然不够优雅,但是实用,像这种获取某个部门或者某个分公司下的所有员工的信息的功能,就不用递归去查找了,直接使用like,只要找到以该编号开头的所有部门就可以了。

       示例涉及到的表有两个,一个是用户表,一个是部门表。两个表需要描述的字段都较多,尤其是用户表,多达好几十个,为了示例简洁,简化后简单的定义如下:

DROP TABLE TBL_USER CASCADE CONSTRAINTS ;

DROP TABLE TBL_DEP CASCADE CONSTRAINTS ;

 

CREATE TABLE TBL_DEP (

    DEPID VARCHAR2(20)  PRIMARY KEY,

    NAME  VARCHAR2(20)

);

CREATE TABLE TBL_USER (

    USERID VARCHAR2(20)  PRIMARY KEY,

    NAME VARCHAR2(20) ,

    DEPID VARCHAR2(20) ,

    SEX VARCHAR2(10) ,

    CONSTRAINT TBL_USER_FK FOREIGN KEY(DEPID)

    REFERENCES TBL_DEP(DEPID)

);

       全部采用大写,是基于Oracle开发的习惯。再来增加点测试数据,SQL如下:

INSERT INTO TBL_DEP VALUES('01','总公司');

INSERT INTO TBL_DEP VALUES('0101','一分公司');

INSERT INTO TBL_DEP VALUES('0102','二分公司');

INSERT INTO TBL_DEP VALUES('010101','开发部');

INSERT INTO TBL_DEP VALUES('010102','测试部');

INSERT INTO TBL_DEP VALUES('010201','开发部');

INSERT INTO TBL_DEP VALUES('010202','客服部');

INSERT INTO TBL_USER VALUES('user0001','张三1','010101','男');

INSERT INTO TBL_USER VALUES('user0002','张三2','010101','男');

INSERT INTO TBL_USER VALUES('user0003','张三3','010102','男');

INSERT INTO TBL_USER VALUES('user0004','张三4','010201','男');

INSERT INTO TBL_USER VALUES('user0005','张三5','010201','男');

INSERT INTO TBL_USER VALUES('user0006','张三6','010202','男');

COMMIT;

       准备好了表结构和测试数据,下面来看看具体的实现示例,为了示例的简洁,直接使用JDBC来完成。

(1)先来定义描述用户数据的对象,示例代码如下:

/**

 * 描述用户数据的对象

 */

public class UserModel {

    /**

     * 用户编号

     */

    private String userId;

    /**

     * 用户姓名

     */

    private String name;

    /**

     * 部门编号

     */

    private String depId;

    /**

     * 性别

     */

    private String sex; 

 

    public String getUserId() {

       return userId;

    }

    public void setUserId(String userId) {

       this.userId = userId;

    }

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

    public String getDepId() {

       return depId;

    }

    public void setDepId(String depId) {

       this.depId = depId;

    }

    public String getSex() {

       return sex;

    }

    public void setSex(String sex) {

       this.sex = sex;

    }  

    public String toString(){

       return "userId="+userId+",name="+name+",depId="

+depId+",sex="+sex+"\n";

    }

}

(2)接下来使用JDBC来实现要求的功能,示例代码如下:

/**

 * 实现示例要求的功能

 */

public class UserManager {

    /**

     * 根据部门编号来获取该部门下的所有人员

     * @param depId 部门编号

     * @return 该部门下的所有人员

     */

    public Collection<UserModel> getUserByDepId(

String depId)throws Exception{

       Collection<UserModel> col = new ArrayList<UserModel>();

       Connection conn = null;

       try{

           conn = this.getConnection();

           String sql = "select * from tbl_user u,tbl_dep d "

              +"where u.depId=d.depId and d.depId like ?";

 

           PreparedStatement pstmt = conn.prepareStatement(sql);

           pstmt.setString(1, depId+"%");

          

           ResultSet rs = pstmt.executeQuery();

           while(rs.next()){

              UserModel um = new UserModel();

              um.setUserId(rs.getString("userId"));

              um.setName(rs.getString("name"));

              um.setDepId(rs.getString("depId"));

              um.setSex(rs.getString("sex"));

             

              col.add(um);

           }

           rs.close();

           pstmt.close();

       }finally{

           conn.close();

       }

       return col;

    }

    /**

     * 获取与数据库的连接

     * @return 数据库连接

     */

    private Connection getConnection() throws Exception {

       Class.forName("你用的数据库对应的JDBC驱动类");

       return DriverManager.getConnection(

              "连接数据库的URL", "用户名", "密码");

    }

}

(3)写个客户端来测试看看,是否能满足功能要求,示例代码如下:

public class Client {

    public static void main(String[] args) throws Exception{

       UserManager userManager = new UserManager();

       Collection<UserModel> col =

userManager.getUserByDepId("0101");

       System.out.println(col);

    }

}

       运行结果如下:

[userId=user0001,name=张三1,depId=010101,sex=男

, userId=user0002,name=张三2,depId=010101,sex=男

, userId=user0003,name=张三3,depId=010102,sex=男

]

       你还可以修改getUserByDepId的参数,试试传递不同的参数,然后再看看输出的值,看看是否正确的实现了要求的功能。

11.1.3  有何问题

上面的实现看起来很简单,功能也正确,但是蕴含一个较大的问题,那就是:当一次性访问的数据条数过多,而且每条描述的数据量又很大的话,那会消耗较多的内存。

前面也说了,对于用户表,事实上是有很多字段的,不仅仅是示例的那么几个,再加上不使用翻页,一次性访问的数据就可能会有很多条。如果一次性需要访问的数据较多的话,内存开销会比较大。

但是从客户使用角度来说,有很大的随机性,客户既可能访问每一条数据,也可能一条都不访问。也就是说,一次性访问很多条数据,消耗了大量内存,但是很可能是浪费掉了,客户根本就不会去访问那么多数据,对于每条数据,客户只需要看看姓名而已。

那么该怎么实现,才能既把多条用户数据的姓名显示出来,而又能节省内存空间,当然还要实现在客户想要看到更多数据的时候,能正确访问到数据呢?

 


---------------------------------------------------------------------------

私塾在线学习网原创内容  跟着cc学设计系列 之 研磨设计模式

研磨设计讨论群【252780326】

原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/5261.html

---------------------------------------------------------------------------

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值