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

 

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 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 query(String sql, Object...params) {
//贾琏欲执事
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List 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;
}

 

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

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

 

 

 

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

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

 

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

 

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

 

 

 

 
作者:一乐乐