组合、继承、内部类什么时候用,该怎么设计?

简介: 组合、继承、内部类什么时候用,该怎么设计?

组合、继承、内部类什么时候用,该怎么设计?


 

1、组合、继承、内部类什么时候用





(1)继承关系:

   ① 就是属于“同族”【类型相同】的类,从两个类的共同代码中抽取出来,作为父类;

   ② 当前类需要用到另外一个类中的所有方法【属于继承或实现了】

   ③ 当前类需要用到另外一个类中大量的属性、方法

(2)组合关系:

   ① 当前类【该类作为一个对象整体很多个方法是需要用到另外一个类中的部分或者全部属性、方法

   ② 当前类只是想对原类进行修饰【作为装饰器】

   ③ 静态代理

(3)内部类:

   ① 内部类跟本类关系密切,一般是只有本类才用得到

一般定义成了private,当然也可以定义成public,当其他类也需要使用到它时,且其他类中需要对它进行一些扩展的话,可以通过继承关系实现

内部类作为一个对象整体, 代码量少、使用频率比较低[一两个地方需要用到]-----》内部类,而非组合关系

 

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

注意:其实内部类和组合关系差不多吧,要么一般使用频率低、且代码量不长,要么与本类系相关,只在本类中使用,就作为内部类啦

但是代码量比较多,就请抽取出去,封装成外部类,然后以组合的方式使用。

 

2、还有一些人性化设计,提醒程序员要进行些操作:

1、在继承关系中,为了提醒程序员不要忘记实现某个重要的方法,将该方法定义成抽象方法,相对应的该类就变成了抽象类啦;

2、在继承关系中,为了避免初始化方法中,程序员忘记传递参数给父类方法,在父类方法中,重载该方法的一个无参方法,然后有参方法中代码调用无参方法

3、在继承关系中,子类通过调用父类的方法拿到某个值,但是方法的调用书写太长,需要重构一下父类,将原本在子类中调用的方法封装到父类,然后子类在调用父类的方法;

 

 

3、继承、组合、内部类分别举个栗子【该过程有人性化设计的点子】:

(1)继承关系[“同族”的类]:(这里就放Servlet2中的代码,Servlet1跟它相同)

■ 一开始Servlet1 和 Servlet2 中的代码:


package com.shan._extends;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class Servlet2 implements Servlet{
    private ServletConfig config;
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;    
    }
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println(config.equals("hello"));    
    }
    @Override
    public ServletConfig getServletConfig() {//返回ServletConfig,将ServletConfig对象暴露给子类访问,为啥?因为config是私有的哈哈
        return this.config;
    }
    @Override
    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }
}


解决:将Servlet1 和 Servlet 中共同的所有方法抽取封装成一个类【然后因为是“同族”------类型相同,使用继承关系】

(这里就放Servlet1中的代码为例,Servlet1跟Servlet2 中抽取封装成一个叫 MyGernericServlet的父类[它的代码跟上面一开始的Servlet2代码一模一样])

package com.shan._extends;
public class Servlet1 extends MyGenericServlet{
}

 

人性化设计之提醒程序员不要忘记实现某个重要的方法,将该方法定义成抽象方法,相对应的该类就变成了抽象类

* 每个Servlet最重要的便是处理请求service方法,所以父类中的service方法需要定义成抽象的

这里就放Servlet1中的代码为例,它的父类MyGernericServle


public class Servlet1 extends MyGenericServlet{
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("hello");
        String value = super.getServletConfig().getInitParameter("wang");
        System.out.println(value);
    }
}


☺ 人性化设计之子类通过调用父类的方法拿到某个值,但是方法的书写太长,需要重构一下父类,将原本在子类中调用的方法封装到父类,然后子类在调用父类的方法;

* String value = super.getServletConfig().getInitParameter("wang"); 想获取参数[这样设计代码的书写太长,可读性差]


● 重构一下封装到父类,(前面的方法super.getServletConfig()想去掉,则应该封装到父类中的getInitParameter方法,原先父类中只有方法:getServletConfig(),还没有 getInitParameter 方法,   看代码书写时就发现:super.getServletConfig().getInitParameter("wang"); 父类中已经有可以获取到该"servletConfig"解决:只需要在父类中书写一个getInitParameter(Strig str)方法,其实方法里边就是 "servletConfig"对象.getInitParameter(str);而 getInitParameter()等都是获取到ServletConfig 中的一些方法,①官方早就有写好了接口ServletConfig,②且后边我们还需要用到该接口的其他的方法,所以这里选择:直接让父类实现官方定义的接口ServletCOnfig


● 最终在子类中效果:String value = super.getInitParameter("wang");
这里就放父类MyGernericServlet中的代码为例Servlet1中仅仅是将service方法 获取参数value的代码设计书写变短,
 变成String value = super.getInitParameter("wang");


abstract class MyGenericServlet implements Servlet, ServletConfig{
    private ServletConfig config;
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;    
    }
    @Override
    abstract public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException ;
    @Override
    public ServletConfig getServletConfig() {//返回ServletConfig
        return this.config;
    }
    @Override
    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }
    /*******************  子类中少写的代码,就交给父类吧  ******************************/
    @Override
    public String getInitParameter(String name) {    //获取初始化参数的信息
        return config.getInitParameter(name);
    }
    @Override
    public String getServletName() {
        return config.getServletName();
    }
    @Override
    public ServletContext getServletContext() {
        return config.getServletContext();
    }
    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }
}

☺ 人性化设计之初始化方法中,避免程序员忘记传递参数给父类方法,在父类方法中重载该方法的一个无参方法,然后有参方法中代码调用无参方法;

这里就放父类MyGernericServlet中的代码与初始化方法有关部分为例,和Servlet1中init方法--重写的是父类那个不带参数的方法)


//父类与初始化方法有关部分abstract class MyGenericServlet implements  Servlet, ServletConfig{
    private ServletConfig config;
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;    
        this.init();
    }
    /**
     * 为了避免人们忘记传入参数【创建了一个无参init()给子类】【*人性化设计*】
     * @throws ServletException
     */
    public void init() throws ServletException{};
}
//Servlet1中init方法public class Servlet1 extends MyGenericServlet{
    @Override
    public void init() throws ServletException {
        System.out.println("子类的初始化工作~");
    }
}



(2)组合关系 [部分或者全部属性、方法]: 举例---设计二叉树的结点类Entry。


(3)内部类(跟本类关系密切),代码量少时,就直接定义成内部类。

   举例1:当其他类也需要使用到它时,且其他类中需要对它进行一些扩展的话,可以通过继承关系实现,
        例如红黑树的结点就是在继承二叉树的Entry基础上加入颜色属性、其他方法的扩展的实现的。


       ■ 举例2:jdbcTempate 类中查询模板(原先代码如下):
这里既有内部类TeacherResultSetHandler、又有外部类IResultHandler的解释


//jdbcTempate 类中查询模板(原先代码如下):
public class JdbcTemplate {
    /**
     * DQL操作的模板    
     * @param sql    DQL操作的SQL模板(带有占位符?)
     * @param params SQL模板中?对应的参数值
     * @return        List结果集
     */
    public static List<Teacher> query(String sql, Object...params) {
        //贾琏欲执事
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<Teacher> teachers = new ArrayList<>();
        try {
            conn = DruidUtil.getConn();
            ps = conn.prepareStatement(sql);
            //设置参数占位符
            for(int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            //结果集处理
            while(rs.next()) {
                Teacher teacher = new Teacher();
                teacher.setId(rs.getLong("id"));
                teacher.setName(rs.getString("name"));
                teacher.setAge(rs.getInt("age"));
                teachers.add(teacher);
            }            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        DruidUtil.close(conn, ps, null);
        return teachers;
    }
}


问题:结果集处理写死了方式一:试图使用泛型(不可,因为后边例如 的rs.getLong("id");  确定下来,跟实体类中的属性有关)


9.png


解决:不要在此处理结果集,应该交给对应的DAO的具体实现类去处理,因为DAO具体实现类中有可以直接拿到实体类的各个属性;

 

 

☺代码重构

代码1在外部封装一个接口IResultSetHandler

IResultHandler定义成外部类的解释因为咱的模板就工具模板,而假如:咱把List handle(ResultSet rs) 定义在了工具模板Template类里的话,

① 它不属于静态属性;(不该在Template类中)

② 其他类也需要实现它(公共属性) ---选择定义在外部


/**
 * 结果处理器,规范处理结果集的方法名称
 * @author Huangyujun
 *
 */
public interface IResultHandler {
    //处理结果集[需要有返回值,即需要得到处理结果]
    List handle(ResultSet rs) throws Exception;
}


 代码2Template类中查询方法:选择传入一个接口对象IResultSetHandler对象然后在处理“结果集处理”位置的代码,调用IResultSetHandler对象.handle();


10.png


 代码3DAO类的具体实现类TeacherDAOImpl中添加内部类TeacherResultSetHandler,然后调用模板工具类Template的查询方法时多加入一个TeacherResultSetHandler对象参数。

TeacherResultSetHandler定义成内部类的解释与本类TeacherDAOImpl息息相关,只在本类使用。

 

 11.png

12.png



目录
相关文章
|
应用服务中间件 网络安全 nginx
快速上手!使用Docker和Nginx部署Web服务的完美指南
快速上手!使用Docker和Nginx部署Web服务的完美指南
|
网络协议 应用服务中间件 nginx
解决 nginx 启动错误host not found in upstream "XXXX.com“
在前置机上利用nginx进行反向代理的时候,我们会配置proxy_pass。在启动nginx的会报如下错误:host not found in upstream "XXXX.com“
6370 0
|
前端开发 JavaScript 数据库
layui入门增删改查
layui入门增删改查
225 0
【每日教程】用中继器做一个三级菜单(含标签)
【每日教程】用中继器做一个三级菜单(含标签)
|
存储 应用服务中间件 调度
在StatefulSet中使用LocalVolume存储卷保持节点一致
StatefulSet是一种有状态服务,其存储卷的使用有多种方式: 使用共享存储,这时在模板中定义一个volume卷,可以给多个pod共享; 每个pod配置独立的存储卷,使用非共享存储(块存储)时需要这样配置,通过配置volumeClaimTemplates实现; 对于StatefulSet使...
5086 0
|
8月前
|
安全 前端开发 Android开发
拥抱国产化:转转APP的鸿蒙NEXT端开发尝鲜之旅
本文将要分享的是转转APP在开发全新鸿蒙NEXT端所遇到的一些问题,对比了鸿蒙开发和 Android、iOS 的不同,总结了这次开发过程中的一些经验等等。希望能带给你启发。
537 0
|
传感器 自动驾驶 算法
本文将探讨无人驾驶汽车如何应对交通拥堵,并指出这种技术可能面临的挑战。
本文将探讨无人驾驶汽车如何应对交通拥堵,并指出这种技术可能面临的挑战。
|
算法 安全 API
淘宝获得淘口令真实URL接口的技术解析
淘口令是淘宝的加密链接,用于商品推广。官方未提供直接解密API,但第三方工具或API能模拟解析。示例代码展示了如何通过第三方接口(需替换为真实接口)获取淘口令所对应的URL、标题和图片信息,但使用时需注意安全风险。
814 2
|
前端开发 Java
springboot项目中外卖用户下单业务功能之需求分析+数据模型+功能开发(详细步骤)
springboot项目中外卖用户下单业务功能之需求分析+数据模型+功能开发(详细步骤)
304 0
|
安全 测试技术 数据库
软件测试案例 | 某教务管理平台系统的系统测试总结报告
集成测试通过之后,各个模块已经被组装成了一个完整的软件包,这时就需要进行系统测试了。传统的系统测试指的是通过集成测试的软件系统,作为计算机系统的一个重要组成部分,其将与计算机硬件、外部设备、支撑软件等其他系统元素组合在一起进行测试,目的在于通过与系统需求定义作比较,发现软件与需求规格不符合或者相矛盾的地方,从而提出更加完善的解决方案。这里特别提出需要软硬件支撑的虚拟现实(Virtual Reality,VR)项目测试的特殊性。
1034 0
软件测试案例 | 某教务管理平台系统的系统测试总结报告

热门文章

最新文章