在 Dart 语言中,可以实现常见的设计模式,这些设计模式提供了在软件开发中解决常见问题的可重用解决方案。设计模式分为三大类:创建型模式、结构型模式 和 行为型模式。我将介绍一些常见的设计模式,并展示如何在 Dart 中实现它们。
单例模式(Singleton Pattern)
目的: 确保一个类只有一个实例,并提供一个全局访问点。 在 Dart 中可以通过工厂构造函数来实现单例模式。工厂构造函数允许返回现有的实例,而不是每次都创建新实例。
class Singleton {
static final Singleton _instance = Singleton._internal();
// 私有构造函数
Singleton._internal();
// 工厂构造函数返回唯一实例
factory Singleton() {
return _instance;
}
void someMethod() {
print('Doing something');
}
}
void main() {
Singleton s1 = Singleton();
Singleton s2 = Singleton();
print(identical(s1, s2)); // 输出:true
}
工厂模式(Factory Pattern)
目的:提供一种创建对象的接口,让子类决定实例化哪一个类。 在 Dart 中,可以通过工厂构造函数或者独立的工厂类来实现工厂模式。
abstract class Shape {
void draw();
}
class Circle implements Shape {
@override
void draw() {
print('Drawing a Circle');
}
}
class Square implements Shape {
@override
void draw() {
print('Drawing a Square');
}
}
class ShapeFactory {
static Shape getShape(String shapeType) {
if (shapeType == 'circle') {
return Circle();
} else if (shapeType == 'square') {
return Square();
} else {
throw Exception('Shape not found');
}
}
}
void main() {
Shape shape1 = ShapeFactory.getShape('circle');
shape1.draw(); // 输出:Drawing a Circle
Shape shape2 = ShapeFactory.getShape('square');
shape2.draw(); // 输出:Drawing a Square
}
建造者模式(Builder Pattern)
目的:用于构建复杂对象,通过一步一步构造,允许分步骤创建对象。 在 Dart 中,建造者模式可以通过类中方法链来实现,常用于构建复杂的对象。
class House {
String? foundation;
String? structure;
String? roof;
@override
String toString() {
return 'House with $foundation foundation, $structure structure, and $roof roof';
}
}
class HouseBuilder {
final House _house = House();
HouseBuilder setFoundation(String foundation) {
_house.foundation = foundation;
return this;
}
HouseBuilder setStructure(String structure) {
_house.structure = structure;
return this;
}
HouseBuilder setRoof(String roof) {
_house.roof = roof;
return this;
}
House build() {
return _house;
}
}
void main() {
House house = HouseBuilder()
.setFoundation('Concrete')
.setStructure('Wood')
.setRoof('Shingle')
.build();
print(house); // 输出:House with Concrete foundation, Wood structure, and Shingle roof
}
适配器模式(Adapter Pattern)
目的:将一个类的接口转换成客户端希望的另一种接口,解决接口不兼容的问题。 在 Dart 中,可以通过创建适配器类,将旧接口适配为新的接口。
class OldAudioPlayer {
void playMp3(String fileName) {
print('Playing MP3 file: $fileName');
}
}
abstract class AudioPlayer {
void play(String fileName);
}
class AudioPlayerAdapter implements AudioPlayer {
final OldAudioPlayer _oldAudioPlayer;
AudioPlayerAdapter(this._oldAudioPlayer);
@override
void play(String fileName) {
_oldAudioPlayer.playMp3(fileName);
}
}
void main() {
OldAudioPlayer oldPlayer = OldAudioPlayer();
AudioPlayer player = AudioPlayerAdapter(oldPlayer);
player.play('song.mp3'); // 输出:Playing MP3 file: song.mp3
}
装饰器模式(Decorator Pattern)
目的:动态地为对象添加功能。装饰器提供了比继承更灵活的扩展功能的方式。 在 Dart 中,可以通过创建装饰类来增强原始类的功能。
abstract class Coffee {
final String description = 'Unknown Coffee';
String getDescription() => description;
double cost();
}
class SimpleCoffee implements Coffee {
@override
String description = 'Simple Coffee';
@override
String getDescription() => description;
@override
double cost() => 5.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String get description => _coffee.description;
@override
String getDescription() => _coffee.getDescription() + ', Milk';
@override
double cost() => _coffee.cost() + 1.5;
}
class SugarDecorator implements Coffee {
final Coffee _coffee;
SugarDecorator(this._coffee);
@override
String get description => _coffee.description;
@override
String getDescription() => _coffee.getDescription() + ', Sugar';
@override
double cost() => _coffee.cost() + 0.5;
}
void main() {
Coffee coffee = SimpleCoffee();
print('${coffee.getDescription()} costs \$${coffee.cost()}');
coffee = MilkDecorator(coffee);
print('${coffee.getDescription()} costs \$${coffee.cost()}');
coffee = SugarDecorator(coffee);
print('${coffee.getDescription()} costs \$${coffee.cost()}');
}
观察者模式(Observer Pattern)
目的:定义对象间一对多的依赖,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
在 Dart 中,可以通过 Stream 来实现观察者模式。注意两点:StreamController 在 dart:async 库里,需要先 import;多个订阅者时要用广播流,单订阅流只能 listen 一次。
import 'dart:async';
class NewsPublisher {
// broadcast:允许多个订阅者同时监听
final _newsController = StreamController<String>.broadcast();
Stream<String> get newsStream => _newsController.stream;
void publishNews(String news) {
_newsController.add(news);
}
}
void main() {
NewsPublisher publisher = NewsPublisher();
publisher.newsStream.listen((news) {
print('Subscriber 1 received news: $news');
});
publisher.newsStream.listen((news) {
print('Subscriber 2 received news: $news');
});
publisher.publishNews('Dart 3.0 Released');
publisher.publishNews('Flutter 3.0 Released');
}
策略模式(Strategy Pattern)
目的:定义一系列算法,并将它们封装在独立的类中,使它们可以互换使用。 在 Dart 中,可以通过接口或者抽象类定义策略,然后通过传递不同的策略类来实现策略模式。
abstract class PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) {
print('Paid \$${amount} with Credit Card');
}
}
class PaypalPayment implements PaymentStrategy {
@override
void pay(double amount) {
print('Paid \$${amount} with Paypal');
}
}
class PaymentContext {
final PaymentStrategy _strategy;
PaymentContext(this._strategy);
void executePayment(double amount) {
_strategy.pay(amount);
}
}
void main() {
PaymentContext context = PaymentContext(CreditCardPayment());
context.executePayment(100.0); // 输出:Paid $100.0 with Credit Card
context = PaymentContext(PaypalPayment());
context.executePayment(200.0); // 输出:Paid $200.0 with Paypal
}
实战案例:我的项目里真正活着的模式
本文列了 7 种 GoF 模式,但说实话,我的项目里叫得上名字的 GoF 模式几乎没用过。真正天天在用的,是框架级的模式:
GetX 三件套(MVC 变体)——旅行类应用、某物流冷链项目 这些项目,页面结构雷打不动:
pages/travel_select_city/
index.dart // 路由入口,只做导出
controller.dart // 状态+业务逻辑
view.dart // 纯 UI
这是 MVC 的 Dart 方言:Controller 管状态,View 只渲染,index.dart 当门面。好处是新来的人(或者三个月后的我)找代码不用猜——改 UI 去 view,改逻辑去 controller。模式的价值不在”优雅”,在”不用动脑子找文件”。
观察者模式——Dart 里它不叫观察者,叫 Stream。SerialService 的 broadcast StreamController 就是观察者模式:数据生产者完全不知道谁在听,UI、算法、日志各听各的。Dart 把观察者模式做成了语言级设施(Stream),所以你不需要手写 addListener/removeListener 那套样板——最好的设计模式,是你感觉不到它存在的那个。
策略模式——_getFieldType 里那一串 is 判断,本质就是策略模式的手动版:根据 value 的类型选不同的生成策略。没写成正经的 Strategy 类,因为分支逻辑太简单,抽成类反而啰嗦。模式是手段不是目的,if-else 能讲清楚的事,别为了”用模式”而用模式。
总结
上面 7 个模式里,我的项目真正天天打交道的只有观察者(Dart 把它做成了 Stream)和 GetX 那套 MVC 方言。GoF 模式当词典查就行——先把”一个类只干一件事”做到,再谈用哪个模式包装。
