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

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

 

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中的代码为例,它的父类MyGernericServlet,仅仅是将service方法定义成抽象,然后它自身变成了抽象方法)
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");  确定下来,跟实体类中的属性有关)

解决:不要在此处理结果集,应该交给对应的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();

 

 

 

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

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

 

 

 

 

 

 

 









 

 

 

 
posted @ 2021-11-21 14:28  一乐乐  阅读(153)  评论(0编辑  收藏  举报