历史文章参见
今天讲讲面向接口编程的核心思想,它可以看到各种设计模式的一种杂糅。
面向接口编程的核心思想
以实际的代码举例子,我最近在写一个安卓的笔记程序,使用到了面向接口的编程方法,下面我以具体的类举例来说明面向接口编程的思想,以及后文解释,面向接口编程可以体现哪些设计模式。
一、依赖接口,而不是具体实现
java
// ❌ 面向具体类(耦合)
public class NoteListManager {
private MainActivity activity; // 直接依赖 MainActivity
}
// ✅ 面向接口(解耦)
public class NoteListManager {
private INoteListCallback callback; // 依赖接口
// 优势:只要接口不变,实现类怎么变都行
}
二、在这个场景中的体现
1. 定义接口(契约)
java
// INoteListCallback.java - 定义"契约"
public interface INoteListCallback {
// 定义需要什么方法,不关心怎么实现
String formatTimestamp(long timestamp);
int dpToPx(int dp);
NoteDbHelper getDbHelper();
}
2. 实现接口(具体实现)
java
// MainActivity.java - 实现接口
public class MainActivity implements INoteListCallback {
// 提供具体实现
@Override
public String formatTimestamp(long timestamp) {
// 具体怎么格式化,MainActivity 自己决定
}
}
3. 使用接口(依赖抽象)
java
// NoteListManager.java - 只依赖接口
public class NoteListManager {
private INoteListCallback callback; // 只依赖接口,不依赖具体类
public void displayNote(Note note) {
// 通过接口调用,不知道具体是哪个类实现的
String time = callback.formatTimestamp(note.getTimestamp());
}
}
三、面向接口编程的优势
1. 解耦
java
// NoteListManager 不知道 MainActivity 的存在
// 它只知道有一个对象实现了 INoteListCallback 接口
// 可以是 MainActivity,也可以是 TestActivity,也可以是 MockActivity
2. 可测试性
java
// 测试时,可以用 Mock 实现
class MockCallback implements INoteListCallback {
@Override
public String formatTimestamp(long timestamp) {
return "Mock时间"; // 测试用的简单实现
}
}
// 测试 NoteListManager
NoteListManager manager = new NoteListManager();
manager.initialize(listView, new MockCallback()); // 用 Mock 测试
3. 可扩展性
java
// 以后可以有不同的实现
class AnotherActivity implements INoteListCallback {
@Override
public String formatTimestamp(long timestamp) {
return "另一种格式"; // 不同的实现方式
}
}
// NoteListManager 不需要改,只需要传入不同的实现
manager.initialize(listView, new AnotherActivity());
四、设计原则体现
1. 依赖倒置原则(DIP)
高层模块(NoteListManager)不应该依赖低层模块(MainActivity)
两者都应该依赖抽象(INoteListCallback)
2. 开闭原则(OCP)
对扩展开放:可以添加新的实现类
对修改关闭:NoteListManager 不需要修改
五、优点总结
markdown
面向接口编程 = 定义接口(契约)
= 实现接口(具体实现)
= 使用接口(依赖抽象)
= 解耦、可测试、可扩展
Callback 就是面向接口编程思想的实际体现。下面我们看看哪些具体的设计模式都跟面向接口编程有关。
与面向接口编程有关的设计模式
常见的一种是策略模式。当然,面向接口编程不仅体现在策略模式 中,还贯穿于多种行为型和结构型模式。
一、在行为型模式中的体现
1. 观察者模式(Observer) ⭐️ 高度体现
java
// 定义接口
interface Observer {
void update(String message);
}
interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 实现接口
class User implements Observer {
@Override
public void update(String message) {
System.out.println("收到消息: " + message);
}
}
// 使用接口
NewsPublisher publisher = new NewsPublisher();
publisher.registerObserver(new User()); // 依赖接口,不依赖具体类
接口思维:Observer是观察者的抽象,Subject是主题的抽象。双方通过接口通信。
2. 命令模式(Command) ⭐️ 核心就是接口
java
// 定义命令接口
interface Command {
void execute();
void undo();
}
// 不同实现
class SaveCommand implements Command {
@Override
public void execute() {
// 保存逻辑
}
}
class DeleteCommand implements Command {
@Override
public void execute() {
// 删除逻辑
}
}
// 使用命令
Button saveBtn = new Button(new SaveCommand()); // 传入接口
接口思维:将请求封装为对象,所有命令都实现Command接口,调用者只依赖接口。
3. 状态模式(State)
java
interface State {
void handle(Context context);
}
class ConcreteStateA implements State {
@Override
public void handle(Context context) {
context.setState(new ConcreteStateB()); // 切换到另一个状态
}
}
接口思维:状态抽象为接口,上下文对象依赖State接口而不是具体状态。
4. 模板方法模式(Template Method)
java
abstract class DataProcessor {
// 模板方法 - 定义算法骨架
public final void process() {
readData(); // 固定步骤
transformData(); // 抽象方法,子类实现
saveData(); // 固定步骤
}
// 抽象方法 - 通过实现来变化
protected abstract void transformData();
}
接口思维:虽然用抽象类,但思想一致 - 定义算法框架,具体步骤由子类实现。
二、在结构型模式中的体现
1. 适配器模式(Adapter) - 核心是统一接口
java
// 目标接口(期望的接口)
interface MediaPlayer {
void play(String audioType, String fileName);
}
// 已有的类,接口不兼容
class Mp4Player {
public void playMp4(String fileName) { /*...*/ }
}
// 适配器 - 实现目标接口,包装已有类
class Mp4Adapter implements MediaPlayer {
private Mp4Player mp4Player;
@Override
public void play(String audioType, String fileName) {
if (audioType.equals("mp4")) {
mp4Player.playMp4(fileName); // 适配
}
}
}
接口思维:通过适配器统一不同系统的接口,客户端只依赖MediaPlayer接口。
2. 桥接模式(Bridge) - 抽象与实现分离
java
// 抽象部分
abstract class Shape {
protected Color color; // 桥接 - 组合Color接口
public Shape(Color color) {
this.color = color;
}
abstract void draw();
}
// 实现部分接口
interface Color {
String fill();
}
// 具体实现
class Red implements Color {
@Override
public String fill() {
return "红色";
}
}
// 使用
Shape circle = new Circle(new Red()); // 通过接口组合
接口思维:将抽象(Shape)与实现(Color)解耦,通过接口组合。
3. 代理模式(Proxy)
java
interface Image {
void display();
}
class RealImage implements Image {
@Override
public void display() {
// 实际加载图片
}
}
class ProxyImage implements Image { // 代理类也实现相同接口
private RealImage realImage;
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(); // 延迟加载
}
realImage.display();
}
}
接口思维:代理类和真实类实现相同的接口,客户端无感知。
三、在创建型模式中的体现
1. 工厂方法模式(Factory Method)
java
interface Product {
void use();
}
interface Creator {
Product createProduct(); // 工厂方法 - 返回接口
}
class ConcreteCreator implements Creator {
@Override
public Product createProduct() {
return new ConcreteProduct(); // 返回具体产品,但声明为Product接口
}
}
接口思维:工厂返回接口类型,客户端不依赖具体产品类。
2. 抽象工厂模式(Abstract Factory)
java
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
interface Button {
void paint();
}
// 客户端代码
class Application {
private Button button;
public Application(GUIFactory factory) { // 依赖工厂接口
button = factory.createButton();
}
}
接口思维:整套产品族通过接口定义,具体工厂实现接口。
四、面向接口编程的层次
第一层:技术实现
java
// 简单的接口定义与实现
interface A { void doSomething(); }
class B implements A { ... }
第二层:设计模式
java
// 模式级别的接口应用
// 1. 策略模式:定义算法族,使其可以互换
// 2. 观察者模式:定义发布-订阅的通信机制
// 3. 命令模式:将请求封装为对象
// 4. 状态模式:将状态抽象为接口
第三层:架构思想
java
// 系统架构层面的接口
// - 依赖倒置:高层模块不依赖低层模块,都依赖抽象
// - 接口隔离:多个专用接口优于一个通用接口
// - 六边形架构:通过端口(接口)与外部世界通信
五、实际项目中的应用启示
何时选择哪种模式?
| 场景 | 适合的模式 | 接口的作用 |
|---|---|---|
| 算法可互换 | 策略模式 | 定义算法接口 |
| 对象状态变化 | 状态模式 | 定义状态接口 |
| 请求需要封装 | 命令模式 | 定义命令接口 |
| 解耦通知机制 | 观察者模式 | 定义观察者接口 |
| 统一不同接口 | 适配器模式 | 定义目标接口 |
| 延迟/控制访问 | 代理模式 | 定义主体接口 |
一个综合示例:电商订单系统
java
// 策略模式:不同的折扣策略
interface DiscountStrategy {
double calculateDiscount(Order order);
}
// 状态模式:订单状态
interface OrderState {
void next(Order order);
void previous(Order order);
void process(Order order);
}
// 观察者模式:订单状态通知
interface OrderObserver {
void update(Order order);
}
// 命令模式:订单操作
interface OrderCommand {
void execute();
void undo();
}
总结
面向接口编程是贯穿所有设计模式的灵魂思想:
- 策略模式:最直接的体现,但远不止于此
- 行为型模式 中大部分都基于接口:
- 观察者、命令、状态、策略等核心都是接口抽象
- 结构型模式 通过接口实现结构解耦:
- 适配器统一接口,桥接分离抽象与实现
- 创建型模式通过接口隐藏创建细节
核心思想 :面向接口编程不仅仅是写一个interface,而是:
- 依赖抽象,而不是具体
- 定义契约,而不是实现
- 关注能做什么,而不是怎么做
这在GoF的23种设计模式中都有深刻体现,是面向对象设计的精髓所在。