我们实现一个功能的时候,很多时候,会先写好大致的框架的代码,然后再引入插件、类库来具体实现代码功能。
假设我正在设计一个媒体播放器模块。我先写好一个【是否正在播放】属性,一个【播放】方法,一个【停止播放】方法,【播放】方法中首先判定是否正在播放,没有播放再播放;【停止播放】方法逻辑趋同。
( C# 开发 )
一开始想用一个接口实现,先写好接口作为框架,但是接口中的方法貌似不建议填充内容,问问了问AI,它告诉我:
业界普遍的做法有以下几种:
方案一:抽象基类(最常用、最清晰)
这是最直接的"空框架"实现。先把通用逻辑写好,具体播放/停止逻辑留空让子类实现。
csharp
public abstract class PlayerBase
{
// 框架层:状态字段
private bool _isPlaying;
// 框架层:只读属性,子类可以获取状态,但最好由基类维护
public bool IsPlaying => _isPlaying;
// 播放方法:框架实现状态判断 + 状态变更
public void Play()
{
if (IsPlaying)
{
Console.WriteLine("已在播放,忽略 Play 调用");
return;
}
// 调用子类实现的具体播放逻辑
OnPlay();
// 播放后修改状态
_isPlaying = true;
}
// 停止方法:类似逻辑
public void Stop()
{
if (!IsPlaying)
{
Console.WriteLine("已停止,忽略 Stop 调用");
return;
}
OnStop();
_isPlaying = false;
}
// 子类需要实现的具体行为
protected abstract void OnPlay();
protected abstract void OnStop();
}
子类实现:
csharp
public class Mp3Player : PlayerBase
{
protected override void OnPlay()
{
Console.WriteLine("MP3 开始播放...");
// 真正的 MP3 播放逻辑
}
protected override void OnStop()
{
Console.WriteLine("MP3 停止播放");
// 真正的停止逻辑
}
}
优点:
-
完美封装"先判断状态再执行"的通用流程
-
基类自行维护
IsPlaying状态,子类无需操心 -
子类只关心具体播放/停止的实现
-
符合模板方法模式
方案二:接口 + 抽象基类(更灵活,适合多人协作)
如果你的框架需要定义多个不相关的播放器族,接口用于对外暴露契约,抽象基类用于代码复用。
csharp
// 1. 定义公共接口(契约)
public interface IMediaPlayer
{
bool IsPlaying { get; }
void Play();
void Stop();
}
// 2. 提供默认实现框架(可复用)
public abstract class PlayerBase : IMediaPlayer
{
private bool _isPlaying;
public bool IsPlaying => _isPlaying;
public void Play()
{
if (_isPlaying) return;
OnPlay();
_isPlaying = true;
}
public void Stop()
{
if (!_isPlaying) return;
OnStop();
_isPlaying = false;
}
protected abstract void OnPlay();
protected abstract void OnStop();
}
优点:
-
接口定义了标准,其它团队可以自己实现接口(不用基类)
-
基类提供了便捷的实现方式(推荐使用基类)
-
扩展性更好
而当我问它,这种先搭框架代码,再考虑具体实现的做法,业界最多的称呼是什么:它说就是
模板方法模式, 是吗?就当是吧,在此记录一下(完)