接口和抽象类是体现面向对象编程中抽象特性的主要概念,可以利用接口来实现面向对象的抽象特性 ,多态特性,基于接口而非实现设计原则,使用抽象类来实现面向对象的继承特性和模板设计模式

1.什么是抽象类和接口,区别在哪?

接下我们分别看下两者的具体实现

// 抽象类

public abstract class Logger {

private String name;

private boolean enabled;

private Level minPermittedLevel;

public Logger(String name, boolean enabled, Level minPermittedLevel) {

this.name = name;

this.enabled = enabled;

this.minPermittedLevel = minPermittedLevel;

}

public void log(Level level, String message) {

boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue());

if (!loggable) return;

doLog(level, message);

}

protected abstract void doLog(Level level, String message);

}

// 抽象类的子类:输出日志到文件

public class FileLogger extends Logger {

private Writer fileWriter;

public FileLogger(String name, boolean enabled,

Level minPermittedLevel, String filepath) {

super(name, enabled, minPermittedLevel);

this.fileWriter = new FileWriter(filepath);

}

@Override

public void doLog(Level level, String mesage) {

// 格式化level和message,输出到日志文件

fileWriter.write(…);

}

}

// 抽象类的子类: 输出日志到消息中间件(比如kafka)

public class MessageQueueLogger extends Logger {

private MessageQueueClient msgQueueClient;

public MessageQueueLogger(String name, boolean enabled,

Level minPermittedLevel, MessageQueueClient msgQueueClient) {

super(name, enabled, minPermittedLevel);

this.msgQueueClient = msgQueueClient;

}

@Override

protected void doLog(Level level, String mesage) {

// 格式化level和message,输出到消息中间件

msgQueueClient.send(…);

}

}

// 接口

public interface Filter {

void doFilter(RpcRequest req) throws RpcException;

}

// 接口实现类:鉴权过滤器

public class AuthencationFilter implements Filter {

@Override

public void doFilter(RpcRequest req) throws RpcException {

//…鉴权逻辑..

}

}

// 接口实现类:限流过滤器

public class RateLimitFilter implements Filter {

@Override

public void doFilter(RpcRequest req) throws RpcException {

//…限流逻辑…

}

}

// 过滤器使用demo

public class Application {

// filters.add(new AuthencationFilter());

// filters.add(new RateLimitFilter());

private List<Filter> filters = new ArrayList<>();

public void handleRpcRequest(RpcRequest req) {

try {

for (Filter filter : fitlers) {

filter.doFilter(req);

}

} catch(RpcException e) {

// …处理过滤结果…

}

// …省略其他处理逻辑…

}

}

对于抽象类,可看出来,抽象类本身是无法实例化,只能继承

抽象类内部可以包含属性和方法,里面的方法可以有已经实现的

子类可以继承抽象,但是必须实现其中的抽象方法

而对于接口,其中不能有属性,方法也不能有代码实现

类实现接口的时候,必须实现接口中声明的所有方法

两者之间的区别,比较大的就是 抽象类更像是is-a的关系,接口更像是has-a的关系,一个是属于,一个是只具有某种功能

2.抽象类和接口能够解决什么编程问题

(1)抽象类

抽象类不能被实例化,只能被继承,只是为了写出来供多个子类来继承,避免编写重复的代码的

如果不使用抽象类,而直接使用基本类来代替,会不可避免的出现一些情况

// 父类:非抽象类,就是普通的类. 删除了log(),doLog(),新增了isLoggable().

public class Logger {

private String name;

private boolean enabled;

private Level minPermittedLevel;

public Logger(String name, boolean enabled, Level minPermittedLevel) {

//…构造函数不变,代码省略…

}

protected boolean isLoggable() {

boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue());

return loggable;

}

}

// 子类:输出日志到文件

public class FileLogger extends Logger {

private Writer fileWriter;

public FileLogger(String name, boolean enabled,

Level minPermittedLevel, String filepath) {

//…构造函数不变,代码省略…

}

public void log(Level level, String mesage) {

if (!isLoggable()) return;

// 格式化level和message,输出到日志文件

fileWriter.write(…);

}

}

// 子类: 输出日志到消息中间件(比如kafka)

public class MessageQueueLogger extends Logger {

private MessageQueueClient msgQueueClient;

public MessageQueueLogger(String name, boolean enabled,

Level minPermittedLevel, MessageQueueClient msgQueueClient) {

//…构造函数不变,代码省略…

}

public void log(Level level, String mesage) {

if (!isLoggable()) return;

// 格式化level和message,输出到消息中间件

msgQueueClient.send(…);

}

}

我们没有在父类中定义log方法.而是只保留了一个isLoggerable()方法

FileLogger和MessageQueuLogger继承了父类,达到了代码复用

但是实际上这样编写,会破坏了多态的特性,如果用一个子类来代替父类,父类调用logger方法的时候,会在编译期间就爆出编译错误

为了避免这种情况的出现,可能使用父类去编写一个空的log方法,但是这样,可能会导致可读性变差

我们可能在使用Logger类的时候,对定义了一个空的log方法而感到疑惑

而且在创建新的子类继承父类的时候,可能会忘记了重写log方法,导致报错了

因为logger现在是一个普通类了,可能被实例化,所以增加了被误调用的风险

那么接口

接口更加像是一种对行为的抽象,让调用者只关心接口的功能,而非如何去实现,从而避免了约定和实现相结合,降低了代码之间的耦合性,提高了可扩展

这也是为什么有着基于接口而非实现编程的思想

3.如何去模拟抽象类和接口两个语法概念?

对于没有抽象类或者接口概念的编程语言来说,如何去实现这两个概念呢

我们对于接口的定义,就是接口中没有成员变量,只有方法声明,实现接口的类必须实现了其中所有声明的方法,只要满足了就可以称为接口,如下就是C++中,抽象类模拟接口

class Strategy { // 用抽象类模拟接口

public:

~Strategy();

virtual void algorithm()=0;

protected:

Strategy();

};

这个类没有定义任何属性,所有的方法都是virtual类型,这个抽象类像是一个接口

那么如果使用普通类来表示抽象类的话,可以为如下情况

让所有继承这个类的子类,必须去实现所有方法,不然运行期就会爆出异常,然后将本来的构造方法设置为protected,让其无法被生成

4.如何判断使用抽象类还是接口?

如果我们需要使用一种is-a的关系,就使用抽象类,如果使用has-a的关系,就是接口

从类的设计上来看

抽象类是一种自下向上的设计思路,将多个子类中重复的代码抽取出来,抽象成为上层的父类

接口正好相反,自下而上的设计思路,先将接口去定义出来,再去实现这些抽象出来的接口

总结一下:

1.抽象类和接口的语法特点

抽象类是一个类,但是不能被实例化,只能被继承,可以拥有属性和方法,方法可以有实现,不实现的方法是抽象方法,子类继承的时候必须实现所有的抽象方法

接口不具有属性,只能声明方法,不能包含代码实现,实现接口的时候,必须实现接口中声明的方法

2.接口和抽象类的存在意义

为了提高代码的复用性,满足抽象的概念

FileInputStream和PipeInputStream都继承了InputStream,提高了复用性,

像InputStream Channle中都实现了Closeable接口,但是实现类之间没有任何关系,这就是一种抽象的概念

一种表示is-a的关系,一种是表示has-a的关系

1.熟悉的编程语言中,是否具有现成的语法去支持接口或者抽象类呢,怎么定义的呢?

2.对接口和抽象类是否有一个新的认识呢?

对于问题一来说,因为一种用的是Java,提供了抽象类和接口的概念,可以直接使用,其中抽象类使用了关键字abstarct,接口使用了关键字interface

对于问题二,经过这几天的学习,有了一个新的认识,一般的web开发,我们都是用的MVC三层框架,基本上写出的代码都是贫血模型的,getter/setter滥用,而且没有真正的去进行抽象化,虽然Spring提供了依赖注入的概念,但是我们书写的抽象类一是命名不规范,二是抽象类和实现等值挂钩,经常出现没有复用性的问题,为了一点小改动而重写接口类,一个抽象类直接对应着一个实现,没有利用多态的思想,写出来的都是面向过程的语言,希望可以在整个课程结束之后,让自己的代码水平有所上升吧

发表评论

邮箱地址不会被公开。 必填项已用*标注