组合、继承、内部类什么时候用,该怎么设计?
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"); 确定下来,跟实体类中的属性有关)
解决:不要在此处理结果集,应该交给对应的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();
■ 代码3:DAO类的具体实现类TeacherDAOImpl中添加内部类TeacherResultSetHandler,然后调用模板工具类Template的查询方法时多加入一个TeacherResultSetHandler对象参数。
●TeacherResultSetHandler定义成内部类的解释:与本类TeacherDAOImpl息息相关,只在本类使用。