这些原则共同目标是让代码更灵活、可维护、可复用,是设计模式(如工厂、策略模式)的基础理论支撑。
SOLID原则解析与对应实现
1. 单一职责原则 (SRP)
- 原则:一个类只负责一个功能。
- 核心思想:一个类应该只有一个引起它变化的原因(即只负责一项功能)。
- 通俗解释 :
就像一个人只专注做一件事(例如厨师只管烹饪,服务员只管点餐),避免"万能类"导致代码难以维护。 - 应用 :
QListWidget
仅负责显示列表项。QScroller
仅负责处理滚动逻辑。- 参数配置单独封装(如
setupInertialScroll
函数)。
cpp
// 职责分离:配置惯性滚动的逻辑单独封装
void setupInertialScroll(QAbstractScrollArea *scrollArea) {
QScroller::grabGesture(scrollArea->viewport(), QScroller::TouchGesture);
QScroller *scroller = QScroller::scroller(scrollArea->viewport());
QScrollerProperties properties;
properties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.2);
scroller->setScrollerProperties(properties);
}
// 主函数仅负责组装
int main() {
QListWidget listWidget;
setupInertialScroll(&listWidget); // 配置滚动逻辑
// ... 添加列表项
}
2. 开闭原则 (OCP)
- 原则:对扩展开放,对修改关闭。
- 核心思想 :软件实体(类、模块等)应对扩展开放 ,对修改关闭。
- 通俗解释 :
就像乐高积木------通过添加新积木(扩展)来构建新功能,而不是拆掉旧积木(修改原有代码)。 - 应用 :
- 通过继承或组合扩展滚动行为,而非修改现有类。
- 示例:自定义
SmoothScrollArea
类,继承QScrollArea
并内置惯性逻辑。
cpp
class SmoothScrollArea : public QScrollArea {
public:
explicit SmoothScrollArea(QWidget *parent = nullptr) : QScrollArea(parent) {
QScroller::grabGesture(viewport(), QScroller::TouchGesture);
QScrollerProperties properties;
properties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.2);
QScroller::scroller(viewport())->setScrollerProperties(properties);
}
};
// 使用扩展类,而非直接修改QScrollArea
SmoothScrollArea scrollArea;
3. 里氏替换原则 (LSP)
- 原则:子类必须能替换父类且行为一致。
- 核心思想:子类必须能够替换父类,且不破坏程序的正确性。
- 通俗解释 :
"鸭子类型"------如果它走路像鸭子、叫声像鸭子,那它就应该能当鸭子用。子类不能违背父类的行为约定。 - 应用 :
SmoothScrollArea
继承QScrollArea
,确保所有父类方法仍有效。- 避免重写核心方法(如
event()
),除非明确需要修改行为。
cpp
// 正确:子类不破坏父类行为
bool SmoothScrollArea::event(QEvent *event) {
if (event->type() == QEvent::Scroll) {
// 处理滚动事件,但不影响其他事件
}
return QScrollArea::event(event); // 确保父类逻辑仍执行
}
4. 接口隔离原则 (ISP)
- 原则:客户端不应依赖不需要的接口。
- 核心思想:客户端不应被迫依赖它不需要的接口。
- 通俗解释 :
就像点餐时只选自己吃的菜,而不是被迫接受一份固定套餐(包含不需要的功能)。 - 应用 :
- 若需要更灵活的滚动控制,将
QScroller
的配置拆分为独立接口。 - 示例:定义
IScrollBehavior
接口,允许动态切换滚动策略。
- 若需要更灵活的滚动控制,将
cpp
class IScrollBehavior {
public:
virtual void applyScroll(QAbstractScrollArea *area) = 0;
};
class InertialScroll : public IScrollBehavior {
public:
void applyScroll(QAbstractScrollArea *area) override {
QScroller::grabGesture(area->viewport(), QScroller::TouchGesture);
// ... 参数配置
}
};
// 使用时注入行为
SmoothScrollArea area;
InertialScroll scrollBehavior;
scrollBehavior.applyScroll(&area);
5. 依赖倒置原则 (DIP)
- 原则:依赖抽象而非具体实现。
- 核心思想 :
- 高层模块不应依赖低层模块,二者都应依赖抽象。
- 抽象不应依赖细节,细节应依赖抽象。
- 通俗解释 :
就像电脑的USB接口(抽象)------鼠标、键盘(细节)都依赖USB标准,而不是电脑直接依赖具体设备。 - 应用 :
- 高层模块(如主窗口)依赖
IScrollBehavior
接口,而非直接调用QScroller
。
- 高层模块(如主窗口)依赖
cpp
class MainWindow : public QMainWindow {
public:
MainWindow(IScrollBehavior *scrollBehavior, QWidget *parent = nullptr)
: QMainWindow(parent), m_scrollBehavior(scrollBehavior) {
QListWidget *list = new QListWidget(this);
m_scrollBehavior->applyScroll(list); // 通过接口配置滚动
}
private:
IScrollBehavior *m_scrollBehavior;
};
// 依赖注入
InertialScroll inertialBehavior;
MainWindow window(&inertialBehavior);
最终代码结构(SOLID合规)
cpp
// 1. 抽象接口(ISP + DIP)
class IScrollBehavior {
public:
virtual void apply(QAbstractScrollArea *area) = 0;
};
// 2. 具体实现(SRP)
class InertialScroll : public IScrollBehavior {
public:
void apply(QAbstractScrollArea *area) override {
QScroller::grabGesture(area->viewport(), QScroller::TouchGesture);
QScrollerProperties props;
props.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.2);
QScroller::scroller(area->viewport())->setScrollerProperties(props);
}
};
// 3. 可扩展的滚动区域(OCP + LSP)
class SmoothScrollArea : public QScrollArea {
public:
explicit SmoothScrollArea(IScrollBehavior *behavior, QWidget *parent = nullptr)
: QScrollArea(parent) {
behavior->apply(this);
}
};
// 4. 高层模块依赖抽象(DIP)
int main() {
InertialScroll inertialBehavior;
SmoothScrollArea scrollArea(&inertialBehavior);
QListWidget listWidget;
SmoothScrollArea listScroll(&inertialBehavior);
// ... 显示窗口
}
优势总结
- 可维护性:滚动逻辑与UI组件解耦,修改滚动行为无需改动控件代码。
- 可扩展性 :新增滚动策略(如弹簧效果)只需实现
IScrollBehavior
。 - 可测试性:可通过Mock接口单独测试滚动行为。
- 灵活性:运行时动态切换滚动效果(如触屏/鼠标模式)。
通过严格遵循SOLID原则,Qt中的惯性滚动实现既满足了功能需求,又保持了代码的高内聚低耦合。