Java 设计模式 接口 组合 委托

本文通过实例探讨了在编程中如何处理代码层次过深和代码复用的问题。文章介绍了四种方法:传统的继承、增加继承层次、使用接口以及组合和委托的结合。通过对比分析,强调了接口、组合和委托在解决代码组织结构问题上的优势,特别是接口用于降低代码层次,而组合和委托则有助于代码复用。最后,展示了如何通过组合和委托实现更灵活的代码结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

写了几年垃圾代码在设计模式上吃过亏,这次看到一篇文章,觉得讲的太好了,就和讲故事一样,逻辑思路很清晰,来来来 ,玩起来,躁动起来…
看着讲了组合和委托,之前对这个概念不太清晰,现在好好梳理一下,主要研究组合和委托解决的是什么问题

为什么少用继承?

假设: Anmial->鸟类->老鹰,大鹏,凤凰
常规认知的情况:鸟类都会飞,
所以class Bird extend Animal{ protected void fly() }
但有一个例外,鸵鸟也是鸟类,但它不会飞!
class Ostrich extends Bird

继承是实现多态的基础:

  • Bird ostrichBird=new Ostrich()
    鸵鸟即是鸟,又是鸵鸟。那么外部调用方就有可能调用 ostrichBird.fly()父类的方法,但是ostrichBird这个对象是不能飞的,这里逻辑就错误了

那怎么办?怎么解决逻辑错误?

方法一:重写方法,抛出异常

在Ostrich中重写fly()方法中抛出throw UnSupportedMethodException(“I can’t fly”); ostrichBird.fly();

public class Ostrich extends Bird{
 @Override
public void fly(){
throw UnSupportedMethodException("I can't fly"); 
}
}

缺陷:
这样虽然能解决问题,但是增加了没有必要的代码,违背了最小知识的设计原则

什么是动态调用(多态)?
子类重写父类方法,多个子类继承了父类,父类引用了子类,父类调用被重写的方法时,就是分别动态调用的各个子类的方法

方法二:增加一层继承关系来区分

做区分:

再抽离一层public class FlyBird,public class UnFlyBird.
继承UnFlyBird,UnFlyBird没有fly()方法

这样虽然能解决问题

但假如有一种鸟类:天鹅,会游泳会飞,鸵鸟不能游泳不能飞
那么就会产生4种抽象类:
1.会游泳会飞,
2.不会游泳会飞,
3.会游泳不会飞,
4.不会游泳不会飞

那么这样继承的层次就太深了:

Animal->FlyBird,UnFlyBird->FlySwimmingBird,UnFlySwimmingBird,FlyUnSwimmingBird,UnFlyUnSwimmingBird

缺陷1

层次太深,不方法维护和查看代码实现,树形结构,从Root到Item,越往下,层次越深

缺陷2:

父类和子类,强耦合,父类更改代码后,所有子类都会收到影响,比如子类方法中 super.fly()父类方法,父类方法改变

方法三:利用has-a Interface来解决这个问题

利用interface的思想:
把原来以继承方式组装的高层次的树形结构代码层次,转化为以接口这种可装载,可卸载的平层(只有一层) ,因为实现类只有两种情况(有该接口:无该接口)
虽然接口解决了:最小知识原则问题,以及强耦合从is-a转变为了has-a的关系
缺陷:
但接口只声明方法,不定义具体实现,所有会下蛋的鸟类都要实现eggLay()方法,而且eggLay()的逻辑都是一样,都是System.out.println(“会下蛋”);。这时就需要把这部分抽离出来,用一个公共的类来实现

//天鹅
class Swan implements Flyable, Swimmingable, EggLayable {
   
    @Override
    public void fly() {System.out.println("会飞");}

    @Override
    public void swimming() {System.out.println("会游泳");}

    @Override
    public void eggLay() {System.out.println("会下蛋");}
}

class Ostrich implements EggLayable { // 鸵鸟
    @Override
    public void eggLay() {System.out.println("会下蛋");}
}

interface Flyable { void fly();}

interface Swimmingable { void swimming();}

interface EggLayable { void eggLay();}


方法四:接口 组合 委托

组合思想:再加一层类(来把多个类相同功能的方法实现出来),然后在子类声明这个类。
委托思想:其实很容易理解,就是把大家都会干的事情"下蛋"这件事情,委托给公共类eggAbility来处理
组合和委托配合起来使用

public class ExtendInterface {
    /**
     * 让天鹅和鸵鸟下蛋 哈哈哈哈...
      */
    public static void main(String[] args) {
        EggLayable swanEggLay = new Swan();
        swanEggLay.eggLay();
        EggLayable ostrichEggLay = new Ostrich();
        ostrichEggLay.eggLay();
    }
}

// 天鹅
class Swan implements Flyable, Swimmingable, EggLayable {
    Flyable flyAbility = new FlyAbility(); // 组合
    Swimmingable swimmingAbility = new SwimmingAbility();// 组合
    EggLayable eggAbility = new EggAbility();// 组合
    
    public Swan(){System.out.println("我是天鹅");}
    
    @Override //委托
    public void fly() { flyAbility.fly();}
    
    @Override //委托
    public void swimming() {  swimmingAbility.swimming();}

    @Override //委托
    public void eggLay() { eggAbility.eggLay();}
}

class Ostrich implements EggLayable { //鸵鸟
    EggLayable eggAbility = new EggAbility();// 组合
    public Ostrich(){ System.out.println("我是鸵鸟"); }
    @Override
    public void eggLay() {eggAbility.eggLay(); } //委托
}

// interface
interface Flyable { void fly();}

interface Swimmingable { void swimming();}

interface EggLayable { void eggLay();}

// 公共方法的实现类
class FlyAbility implements Flyable {
    @Override
    public void fly() {System.out.println("飞一会儿"); }
}
// 公共方法的实现类
class SwimmingAbility implements Swimmingable {
    @Override
    public void swimming() { System.out.println("游泳"); }
}
// 公共方法的实现类
class EggAbility implements EggLayable {
    @Override
    public void eggLay() {System.out.println("下一颗蛋"); }
}

总结

代码组织结构中经常会出现两类问题:代码层次过深问题,代码不能复用问题

Interface:解决代码层次问题
组合和委托:解决代码复用问题

<think>嗯,用户想了解Excel中的VLOOKUP函数的用法和参数含义。首先,我需要回忆一下VLOOKUP的基本结构。VLOOKUP是垂直查找函数,用于在表格的首列查找指定的值,然后返回该行中指定列的数据。它的四个参数分别是查找值、表格范围、列索引号和匹配模式。 接下来,我得确认每个参数的具体作用。第一个参数是lookup_value,也就是用户要查找的值,比如产品ID或者姓名。第二个参数是table_array,即查找的范围,这里要注意的是,查找值必须位于这个范围的第一列,否则函数无法正确工作。第三个参数是col_index_num,也就是用户想要返回的数据在表格范围中的第几列,这个列数是从表格范围的第一列开始算起的,不是整个工作表的列。第四个参数是range_lookup,决定是精确匹配还是近似匹配,通常用FALSE或0来指定精确匹配,避免错误。 然后,用户可能需要一个具体的例子来理解如何使用。比如,假设有一个产品价格表,用户想根据产品ID查找对应的价格。这时候,VLOOKUP就可以在ID列查找,然后返回价格列的数据。同时,要提醒用户注意表格范围最好使用绝对引用,比如$A$2:$C$100,这样在拖动公式时范围不会变化。 可能遇到的问题也需要考虑。比如,如果查找值不在表格的第一列,或者列索引号超过了表格的列数,函数会返回错误。此外,数据格式不一致也可能导致问题,比如查找值是数字,而表格中是文本格式的数字,这时候需要统一格式。还有,当出现#N/A错误时,可能是查找值不存在,或者匹配模式设置不正确,需要检查参数是否正确。 用户可能还会问到如何避免常见的错误,比如部分数据匹配出错的情况。这时候需要检查是否有多余的空格,或者数据格式不一致,或者表格范围没有锁定导致拖动时偏移。此外,XLOOKUP作为VLOOKUP的升级版,可能在某些情况下更方便,比如可以向左查找或者返回多列数据,这也是可以提及的内容。 最后,整理步骤,从基础语法到参数说明,再到具体案例,逐步引导用户理解如何使用VLOOKUP函数。同时,提供常见错误和解决方法,帮助用户在实际应用中避免问题。如果有必要,还可以比较VLOOKUP和其他查找函数的区别,比如HLOOKUP或XLOOKUP,但根据用户的问题,主要集中在VLOOKUP本身即可。</think>### Excel VLOOKUP函数详解 #### 一、基础语法与参数说明 VLOOKUP函数的语法为: $$=VLOOKUP(lookup\_value, table\_array, col\_index\_num, [range\_lookup])$$ 包含4个参数: 1. **lookup_value**(必填):要查找的值(如单元格引用或具体值) 2. **table_array**(必填):包含数据的表格范围(必须包含查找列和返回列) 3. **col_index_num**(必填):返回值所在列的序号(从table_array第一列开始计数) 4. **range_lookup**(可选):匹配类型 - `TRUE`/`1`:近似匹配(默认值,需数据升序排列) - `FALSE`/`0`:精确匹配(常用选项) [^1][^2] #### 二、使用步骤演示(工资表查询案例) 假设需要根据员工编号查询工资: 1. 建立查询单元格(如`B12`) 2. 输入公式: ```excel =VLOOKUP(A12, $A$2:$D$100, 4, 0) ``` - `A12`:待查询的员工编号 - `$A$2:$D$100`:锁定数据区域(绝对引用) - `4`:返回第4列(工资列) - `0`:精确匹配 [^2][^3] #### 三、常见错误与解决方法 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | #N/A | 查找值不存在 | 检查数据源或改用`IFERROR`容错 | | #REF! | 列序号超出范围 | 确认col_index_num ≤ 表格列数 | | 部分匹配失败 | 数据格式不一致 | 统一数值/文本格式 | | 结果错位 | 表格未锁定 | 使用`$`符号固定区域引用 | [^3][^4] #### 四、进阶技巧 1. **多条件查询**: 使用辅助列合并多个条件字段 ```excel =VLOOKUP(A2&B2, $D$2:$F$100, 3, 0) ``` 2. **通配符匹配**: `"*"`匹配任意字符,`"?"`匹配单个字符 ```excel =VLOOKUP("张*", $A$2:$C$100, 3, 0) ``` 3. **跨表查询**: 引用其他工作表数据 ```excel =VLOOKUP(A2, Sheet2!$A$2:$D$100, 4, 0) ``` [^1][^4]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值