Dart: تحسين المشروع ونشره — خط أنابيب البيانات

آخر تحديث: 2026-08-26

الكود الذي يعمل ليس كافياً؛ الكود الذي يعمل بسرعة واستقرار وأمان هو تسليم المهندس. — تشارلي

1. ما ستتعلمه


2. قصة مطور حقيقية

(1) نقطة الألم: يعمل لكن يعمل ببطء

عالج خط أنابيب البيانات 10 آلاف طلب في 0.3 ثانية فقط. عندما طبقت أليس اختبار إجهاد على مجموعة بيانات مكونة من مليون طلب، استغرق الأمر 45 ثانية. حدد بوب المشاكل: تقسيم Isolate غير متساوٍ (واحد فقط من 4 أنوية كان يعمل)، عدم وجود ضغط عكسي في Streams تسبب في ارتفاع الذاكرة إلى 2 جيجابايت، ومنتج 80 ميجابايت تم تجميعه في وضع التصحيح.

(2) الحل المحسّن

تقسيم Isolate الديناميكي وازن الحمل عبر 4 أنوية، إضافة الضغط العكسي إلى Streams أبقى الذاكرة تحت 200 ميجابايت، تجميع AOT قلل المخرجات إلى 15 ميجابايت، وأتمت CI/CD الاختبار والنشر.

100%
flowchart LR
  A[الكود مكتمل] --> B[تحسين الأداء]
  B --> C[ضبط Isolate]
  B --> D[ضغط Stream العكسي]
  B --> E[تحليل الذاكرة]
  C --> F[تجميع AOT]
  F --> G[CI/CD]
  G --> H[GitHub Actions]
  H --> I[النشر على pub.dev]
  I --> J["v1.0.0"]
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

(3) الفوائد


3. تحليل الأداء وتحسينه

(1) توازن تحميل Isolate

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: تقسيم ديناميكي مقابل ثابت

DART
import 'dart:isolate';

// سيء: التقسيم الثابت - عمل غير متساوٍ
List<List<T>> staticChunk<T>(List<T> data, int chunks) {
  final size = (data.length / chunks).ceil();
  return [
    for (var i = 0; i < chunks; i++)
      data.sublist(i * size, ((i + 1) * size).clamp(0, data.length))
  ];
}

// جيد: سرقة العمل الديناميكية مع SendPort
class DynamicIsolatePool {
  final int _poolSize;
  var _pendingChunks = <List<Map<String, dynamic>>>[];
  final _results = <AnalysisResult>[];
  var _activeWorkers = 0;
  late Completer<List<AnalysisResult>> _completer;

  DynamicIsolatePool({int poolSize = 4}) : _poolSize = poolSize;

  Future<List<AnalysisResult>> process(
    List<Map<String, dynamic>> allOrders, {
    int chunkSize = 50000,
  }) async {
    _pendingChunks = [
      for (var i = 0; i < allOrders.length; i += chunkSize)
        allOrders.sublist(i, (i + chunkSize).clamp(0, allOrders.length))
    ];

    _completer = Completer<List<AnalysisResult>>();
    _activeWorkers = 0;

    final workers = <_Worker>[];
    for (var i = 0; i < _poolSize && _pendingChunks.isNotEmpty; i++) {
      final chunk = _pendingChunks.removeAt(0);
      workers.add(await _spawnWorker(chunk));
      _activeWorkers++;
    }

    return _completer.future;
  }

  Future<_Worker> _spawnWorker(List<Map<String, dynamic>> initialChunk) async {
    final receivePort = ReceivePort();
    final isolate = await Isolate.spawn(_workerEntry, receivePort.sendPort);
    final sendPort = await receivePort.first as SendPort;

    final responsePort = ReceivePort();
    sendPort.send(_WorkMessage(data: initialChunk, responsePort: responsePort.sendPort));

    responsePort.listen((result) {
      _results.add(result as AnalysisResult);
      _activeWorkers--;

      if (_pendingChunks.isNotEmpty) {
        final nextChunk = _pendingChunks.removeAt(0);
        sendPort.send(_WorkMessage(data: nextChunk, responsePort: responsePort.sendPort));
        _activeWorkers++;
      } else if (_activeWorkers == 0) {
        isolate.kill(priority: Isolate.immediate);
        receivePort.close();
        responsePort.close();
        _completer.complete(_results);
      }
    });

    return _Worker(isolate: isolate, sendPort: sendPort);
  }

  static void _workerEntry(SendPort mainSendPort) {
    final receivePort = ReceivePort();
    mainSendPort.send(receivePort.sendPort);

    receivePort.listen((message) {
      if (message is _WorkMessage) {
        final orders = message.data.map((m) => Order(
          id: m['id'] as String,
          amount: (m['amount'] as num).toDouble(),
          status: m['status'] as String,
          category: m['category'] as String,
        )).toList();

        final result = const OrderAnalyzer().analyze(orders);
        message.responsePort.send(result);
      }
    });
  }
}

class _Worker {
  final Isolate isolate;
  final SendPort sendPort;
  const _Worker({required this.isolate, required this.sendPort});
}

class _WorkMessage {
  final List<Map<String, dynamic>> data;
  final SendPort responsePort;
  const _WorkMessage({required this.data, required this.responsePort});
}
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.
منطقة التحسين قبل بعد التحسن
استراتيجية التقسيم تقسيم متساوٍ ثابت سرقة عمل ديناميكية أسرع بنسبة 30%
حجم الجزء 1 جزء/عامل 50 ألف عنصر/جزء الذاكرة للنصف
عدد العمال ثابت 4 متكيف مع أنوية CPU أسرع على متعدد الأنوية

(2) ضغط Stream العكسي

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: التحكم في الضغط العكسي

DART
import 'dart:async';

class BackpressurePipeline {
  final DataSource _source;
  final OrderParser _parser;
  final int _maxConcurrent;

  BackpressurePipeline({
    required DataSource source,
    required OrderParser parser,
    int maxConcurrent = 1000,
  })  : _source = source,
        _parser = parser,
        _maxConcurrent = maxConcurrent;

  Future<AnalysisResult> execute() async {
    final orders = <Order>[];
    var pending = 0;
    final completer = Completer<void>();

    final subscription = _source.readLines().listen(
      (line) {
        try {
          orders.add(_parser.parse(line));
        } on FormatException {
          return;
        }

        pending++;
        if (pending >= _maxConcurrent) {
          subscription.pause();
        }
      },
      onDone: () {
        completer.complete();
      },
      onError: (e) {
        completer.completeError(e);
      },
    );

    await completer.future;

    return const OrderAnalyzer().analyze(orders);
  }

  // Stream قابل للإيقاف مع التحكم في المخزن المؤقت
  Stream<Order> streamWithBackpressure() async* {
    var buffer = <Order>[];

    await for (final line in _source.readLines()) {
      try {
        buffer.add(_parser.parse(line));
      } on FormatException {
        continue;
      }

      if (buffer.length >= _maxConcurrent) {
        for (final order in buffer) {
          yield order;
        }
        buffer = [];
      }
    }

    for (final order in buffer) {
      yield order;
    }
  }
}
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.
استراتيجية الضغط العكسي الوصف حالة الاستخدام
pause/resume يوقف تدفق المصدر المستهلك يعالج ببطء
حد المخزن المؤقت مخزن مؤقت بحجم ثابت استخدام ذاكرة قابل للتحكم
إسقاط الأحدث يتجاهل البيانات التي تتجاوز الحد فقدان محتمل في المراقبة في الوقت الفعلي

(3) تحسين الذاكرة

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: تقنيات تحليل الذاكرة

DART
import 'dart:math';

class MemoryOptimizer {
  // استخدام iterables بدلاً من القوائم حيثما أمكن
  static Iterable<Order> lazyParse(Iterable<String> lines, OrderParser parser) sync* {
    for (final line in lines) {
      try {
        yield parser.parse(line);
      } on FormatException {
        continue;
      }
    }
  }

  // المعالجة بأجزاء لتحديد الذاكرة
  static Future<AnalysisResult> processInChunks(
    Stream<String> lines,
    OrderParser parser, {
    int chunkSize = 100000,
  }) async {
    var chunk = <Order>[];
    var totalResult = _EmptyAnalysisResult();

    await for (final line in lines) {
      try {
        chunk.add(parser.parse(line));
      } on FormatException {
        continue;
      }

      if (chunk.length >= chunkSize) {
        final partial = const OrderAnalyzer().analyze(chunk);
        totalResult = totalResult.merge(partial);
        chunk = [];
      }
    }

    if (chunk.isNotEmpty) {
      final partial = const OrderAnalyzer().analyze(chunk);
      totalResult = totalResult.merge(partial);
    }

    return totalResult.toAnalysisResult();
  }
}

class _EmptyAnalysisResult {
  int totalOrders = 0;
  int completedOrders = 0;
  double revenue = 0;
  double tax = 0;
  final Map<String, double> revenueByCategory = {};

  _EmptyAnalysisResult merge(AnalysisResult other) {
    totalOrders += other.totalOrders;
    completedOrders += other.completedOrders;
    revenue += other.revenue;
    tax += other.tax;
    for (final entry in other.revenueByCategory.entries) {
      revenueByCategory.update(entry.key, (v) => v + entry.value, ifAbsent: () => entry.value);
    }
    return this;
  }

  AnalysisResult toAnalysisResult() => AnalysisResult(
    totalOrders: totalOrders,
    completedOrders: completedOrders,
    revenue: revenue,
    tax: tax,
    total: revenue + tax,
    revenueByCategory: Map.unmodifiable(revenueByCategory),
  );
}
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.
تقنية التحسين التأثير حالة الاستخدام
Iterable كسول بدون تحميل مسبق لجميع البيانات تمرير البيانات عنصراً بعنصر من المصدر إلى المستهلك
المعالجة بأجزاء استخدام ذاكرة ثابت معالجة ملايين السجلات
دمج النتائج نتائج وسيطة صغيرة تجميع الإحصائيات بعد التقسيم

4. تجميع AOT وتخفيف المخرجات

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: أوامر التجميع والتحسين

BASH
# بناء التصحيح (كبير، يتضمن معلومات التصحيح)
dart compile exe bin/data_pipeline.dart -o build/data_pipeline_debug

# بناء الإصدار (محسّن، أصغر)
dart compile exe bin/data_pipeline.dart -o build/data_pipeline \
  --define=MODE=release

# التحقق من حجم الثنائي
ls -lh build/

# إزالة رموز التصحيح (تخفيض إضافي في الحجم)
# على Linux/macOS:
strip build/data_pipeline
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.
وضع التجميع الأمر حجم المخرجات سرعة البدء
JIT (dart run) dart run bin/main.dart 0 (يشغل المصدر) بطيء
AOT exe dart compile exe ~15 ميجابايت سريع
AOT aot-snapshot dart compile aot-snapshot ~8 ميجابايت الأسرع (يتطلب dartaotruntime)

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: تكوين وقت الترجمة

DART
// استيرادات شرطية لـ tree shaking
const _isRelease = bool.fromEnvironment('MODE.release');

void log(String message) {
  if (!_isRelease) {
    print('[DEBUG] $message');
  }
}

// ستتم إزالة هذه الفئة بالكامل في وضع الإصدار
class DebugLogger {
  void log(String message) {
    if (!_isRelease) {
      print('[DEBUG] ${DateTime.now()}: $message');
    }
  }
}
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

5. CI/CD — GitHub Actions

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: تكوين CI الكامل

YAML
# .github/workflows/ci.yml
name: DataPipeline CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  analyze-and-test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        sdk: [stable, beta]

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1
        with:
          sdk: ${{ matrix.sdk }}

      - name: Install dependencies
        run: dart pub get

      - name: Verify formatting
        run: dart format --output=none --set-exit-if-changed .

      - name: Analyze code
        run: dart analyze --fatal-infos

      - name: Run tests
        run: dart test --coverage=coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: coverage/lcov.info

  build-and-release:
    needs: analyze-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1

      - name: Install dependencies
        run: dart pub get

      - name: Compile AOT binary
        run: |
          dart compile exe bin/data_pipeline.dart -o build/data_pipeline

      - name: Run integration test
        run: |
          ./build/data_pipeline analyze --source csv --input test/fixtures/sample.csv

      - name: Upload binary artifact
        uses: actions/upload-artifact@v4
        with:
          name: data_pipeline-linux
          path: build/data_pipeline

  publish:
    needs: build-and-release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1

      - name: Install dependencies
        run: dart pub get

      - name: Publish to pub.dev
        run: dart pub publish --force
        env:
          PUB_CREDENTIALS: ${{ secrets.PUB_CREDENTIALS }}
مرحلة CI الفحوصات التي يتم إجراؤها الإجراء عند الفشل
التنسيق dart format تنسيق تلقائي لـ PR
التحليل الثابت dart analyze حظر الدمج
اختبارات الوحدة dart test حظر الدمج
اختبار التكامل ثنائي AOT شامل حظر النشر
النشر dart pub publish يتم تشغيله فقط بعلامة

6. النشر على pub.dev

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: تكوين النشر في pubspec.yaml

YAML
name: data_pipeline
description: E-commerce data analytics CLI tool - process million-level orders with Stream + Isolate
version: 1.0.0

homepage: https://github.com/bob/datapipeline
repository: https://github.com/bob/datapipeline
issue_tracker: https://github.com/bob/datapipeline/issues

environment:
  sdk: ^3.0.0

dependencies:
  args: ^2.4.2
  http: ^1.1.0
  csv: ^6.0.0

dev_dependencies:
  test: ^1.24.0
  build_runner: ^2.4.0
  json_serializable: ^6.7.0

executables:
  data_pipeline: data_pipeline

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: CHANGELOG.md

MARKDOWN
# CHANGELOG

---

## ملاحظات الإصدار

### (1) 1.0.0 - 2025-01-15

#### الميزات
- دعم مصادر بيانات CSV و JSON
- خط أنابيب قائم على Stream مع ضغط عكسي
- Isolate Pool للمعالجة المتوازية
- CLI مع الأوامر الفرعية analyze/export/validate
- مطابقة الأنماط + بنية الصنف المختوم

### (2) الأداء
- معالجة مليون طلب في ~8 ثوانٍ (4 أنوية)
- استخدام الذاكرة تحت 200 ميجابايت لمليون سجل

### (3) الاختبار
- تغطية كود 95%
- اختبارات الوحدة + التكامل + E2E

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: قائمة مراجعة ما قبل النشر

BASH
# قائمة مراجعة ما قبل النشر

# 1. تشغيل جاف للنشر
dart pub publish --dry-run

# 2. التحقق من النقاط على pub.dev
dart pub publish --dry-run 2>&1 | grep -E "score|warning|error"

# 3. توليد وثائق API
dart doc .

# 4. التحقق من محتويات الحزمة
dart pub pack  # ينشئ .tar.gz للفحص

# 5. النشر النهائي
dart pub publish
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.
خطوة النشر الأمر الغرض
تشغيل جاف dart pub publish --dry-run التحقق من محتويات الحزمة
توليد الوثائق dart doc . وثائق API
فحص الإصدار pubspec.yaml version الإصدار الدلالي
النشر dart pub publish الدفع إلى pub.dev

7. توليد وثائق API

▶ مثال

TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

: معايير تعليقات الوثائق

DART
/// يحلل بيانات طلبات التجارة الإلكترونية وينتج نتائج إحصائية.
///
/// استخدم [OrderAnalyzer] كنقطة دخول أساسية لتحليل البيانات.
/// كون [taxRate] بناءً على المنطقة المستهدفة.
///
/// مثال:
/// ```dart
/// final analyzer = OrderAnalyzer(taxRate: 0.08);
/// final result = analyzer.analyze(orders);
/// print('Revenue: \$${result.revenue} USD');
/// ```
///
/// انظر أيضاً:
/// - [AnalysisResult] لبنية المخرجات
/// - [Order] لنموذج الإدخال
class OrderAnalyzer {
  /// ينشئ محللاً بمعدل الضريبة [taxRate] المعطى.
  ///
  /// الافتراضي 0.08 (معدل ضريبة US 8%).
  /// استخدم معدلات خاصة بالمنطقة: US=0.08, EU=0.20, UK=0.15.
  const OrderAnalyzer({this.taxRate = 0.08});

  /// معدل الضريبة المطبق على الطلبات المكتملة.
  final double taxRate;

  /// يحلل قائمة [orders] ويُرجع إحصائيات مجمعة.
  ///
  /// فقط الطلبات ذات الحالة 'completed' تُدرج في حسابات الإيرادات.
  /// الطلبات ذات الحالات الأخرى تُعد في [AnalysisResult.totalOrders]
  /// لكنها مستبعدة من الإيرادات.
  AnalysisResult analyze(List<Order> orders) {
    // ... التنفيذ
  }
}
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

8. المثال الكامل: قياس الأداء

DART
// ============================================
// قياس أداء خط أنابيب البيانات
// اختبار الإجهاد الشامل لأليس
// ============================================

import 'dart:async';
import 'dart:io';

class Benchmark {
  final String name;
  final Stopwatch _stopwatch;

  Benchmark(this.name) : _stopwatch = Stopwatch();

  Future<T> run<T>(Future<T> Function() action) async {
    _stopwatch.reset();
    _stopwatch.start();
    final result = await action();
    _stopwatch.stop();
    print('$name: ${_stopwatch.elapsedMilliseconds}ms');
    return result;
  }
}

// توليد بيانات الاختبار
List<String> generateOrders(int count) {
  final categories = ['Electronics', 'Clothing', 'Books', 'Home', 'Sports'];
  final statuses = ['completed', 'completed', 'completed', 'pending', 'cancelled'];
  final regions = ['US', 'EU', 'UK', 'JP'];

  return List.generate(count, (i) {
    final category = categories[i % categories.length];
    final status = statuses[i % statuses.length];
    final region = regions[i % regions.length];
    final amount = (100 + (i * 37) % 5000).toDouble();
    return 'ORD-${(i + 1).toString().padLeft(7, '0')},$amount,$status,$category,$region';
  });
}

void main() async {
  print('=== DataPipeline Performance Benchmark ===\n');

  final sizes = [10000, 100000, 1000000];

  for (final size in sizes) {
    final label = size >= 1000000 ? '${size ~/ 1000000}M' : '${size ~/ 1000}K';
    print('--- $label orders ---');

    // توليد وحفظ في ملف مؤقت
    final data = generateOrders(size);
    final tempFile = File('${Directory.systemTemp.path}/bench_${label}.csv');
    await tempFile.writeAsString(data.join('\n'));

    // القياس 1: معالجة Stream
    final streamResult = await Benchmark('Stream ($label)').run(() async {
      final source = CsvSource(path: tempFile.path);
      final parser = const CsvOrderParser();
      final pipeline = Pipeline(source: source, parser: parser);
      return pipeline.execute();
    });

    // القياس 2: معالجة بأجزاء موفرة للذاكرة
    final chunkedResult = await Benchmark('Chunked ($label)').run(() async {
      final source = CsvSource(path: tempFile.path);
      return MemoryOptimizer.processInChunks(
        source.readLines(),
        const CsvOrderParser(),
        chunkSize: 50000,
      );
    });

    // التحقق من تطابق النتائج
    final streamRev = streamResult.revenue.toStringAsFixed(2);
    final chunkedRev = chunkedResult.revenue.toStringAsFixed(2);
    print('Revenue match: ${streamRev == chunkedRev ? "PASS" : "FAIL"}');
    print('Revenue: \$${streamRev} USD\n');

    await tempFile.delete();
  }

  // القياس 3: فحص حجم ثنائي AOT
  print('--- Build Artifacts ---');
  final exe = File('build/data_pipeline');
  if (await exe.exists()) {
    final sizeKB = await exe.length() ~/ 1024;
    print('AOT binary: ${sizeKB}KB');
  } else {
    print('AOT binary: not built (run: dart compile exe bin/data_pipeline.dart)');
  }

  print('\n=== Benchmark Complete ===');
  print('Alice acceptance criteria: 1M orders < 10s, memory < 300MB, binary < 20MB');
}

// ---- stubs مطلوبة (مستوردة من الدروس السابقة) ----

sealed class DataSource {
  const DataSource();
  String get displayName;
  Stream<String> readLines();
}

class CsvSource extends DataSource {
  final String path;
  const CsvSource({required this.path});
  @override
  String get displayName => 'CSV: $path';
  @override
  Stream<String> readLines() =>
    File(path).openRead().transform(utf8.decoder).transform(const LineSplitter());
}

abstract class OrderParser {
  const OrderParser();
  Order parse(String raw);
}

class CsvOrderParser extends OrderParser {
  const CsvOrderParser();
  @override
  Order parse(String raw) {
    final parts = raw.split(',');
    if (parts.length < 4) throw FormatException('Invalid row');
    return Order(
      id: parts[0].trim(),
      amount: double.parse(parts[1].trim()),
      status: parts[2].trim(),
      category: parts[3].trim(),
      region: parts.length > 4 ? parts[4].trim() : 'US',
    );
  }
}

class Order {
  final String id;
  final double amount;
  final String status;
  final String category;
  final String region;
  const Order({required this.id, required this.amount, required this.status, required this.category, this.region = 'US'});
  double get tax => amount * 0.08;
}

class AnalysisResult {
  final int totalOrders;
  final int completedOrders;
  final double revenue;
  final double tax;
  final double total;
  final Map<String, double> revenueByCategory;
  const AnalysisResult({required this.totalOrders, required this.completedOrders, required this.revenue, required this.tax, required this.total, required this.revenueByCategory});
}

class OrderAnalyzer {
  const OrderAnalyzer();
  AnalysisResult analyze(List<Order> orders) {
    final completed = orders.where((o) => o.status == 'completed').toList();
    final revenue = completed.fold<double>(0, (s, o) => s + o.amount);
    final tax = completed.fold<double>(0, (s, o) => s + o.tax);
    final byCategory = <String, double>{};
    for (final o in completed) {
      byCategory.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
    }
    return AnalysisResult(totalOrders: orders.length, completedOrders: completed.length, revenue: revenue, tax: tax, total: revenue + tax, revenueByCategory: byCategory);
  }
}

class Pipeline {
  final DataSource source;
  final OrderParser parser;
  const Pipeline({required this.source, required this.parser});
  Future<AnalysisResult> execute() async {
    final orders = <Order>[];
    await for (final line in source.readLines()) {
      try { orders.add(parser.parse(line)); } on FormatException { continue; }
    }
    return const OrderAnalyzer().analyze(orders);
  }
}

class MemoryOptimizer {
  static Future<AnalysisResult> processInChunks(Stream<String> lines, OrderParser parser, {int chunkSize = 50000}) async {
    var chunk = <Order>[];
    int totalOrders = 0, completedOrders = 0;
    double revenue = 0, tax = 0;
    final byCategory = <String, double>{};

    await for (final line in lines) {
      try { chunk.add(parser.parse(line)); } on FormatException { continue; }
      if (chunk.length >= chunkSize) {
        final r = const OrderAnalyzer().analyze(chunk);
        totalOrders += r.totalOrders;
        completedOrders += r.completedOrders;
        revenue += r.revenue;
        tax += r.tax;
        for (final e in r.revenueByCategory.entries) {
          byCategory.update(e.key, (v) => v + e.value, ifAbsent: () => e.value);
        }
        chunk = [];
      }
    }
    if (chunk.isNotEmpty) {
      final r = const OrderAnalyzer().analyze(chunk);
      totalOrders += r.totalOrders;
      completedOrders += r.completedOrders;
      revenue += r.revenue;
      tax += r.tax;
      for (final e in r.revenueByCategory.entries) {
        byCategory.update(e.key, (v) => v + e.value, ifAbsent: () => e.value);
      }
    }
    return AnalysisResult(totalOrders: totalOrders, completedOrders: completedOrders, revenue: revenue, tax: tax, total: revenue + tax, revenueByCategory: byCategory);
  }
}
TEXT 📖 للعرض فقط
> **الإخراج:** نفّذ محلياً في DartPad أو مع `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً مع إصدارات SDK.

9. تقرير قبول أليس

معايير القبول الهدف الفعلي الحالة
وقت معالجة مليون طلب < 10 ثوانٍ ~8 ثوانٍ PASS
ذروة استخدام الذاكرة < 300 ميجابايت ~180 ميجابايت PASS
حجم مخرجات AOT < 20 ميجابايت ~15 ميجابايت PASS
تغطية الاختبار > 90% 95% PASS
dart analyze 0 تحذيرات 0 PASS
dart format 100% 100% PASS
تغطية الوثائق 100% من API العامة 100% PASS
نقاط pub.dev > 120 140 PASS

أليس: "تم قبول DataPipeline v1.0! تم معالجة مليون طلب في 8 ثوانٍ، والذاكرة بقيت تحت 200 ميجابايت، ومخرجات بناء AOT مضغوطة، وأتمتة CI/CD مكتملة. جاهز للإصدار!"


❓ أسئلة شائعة

س: لماذا مخرجات بناء AOT كبيرة جداً؟ ج: تتضمن مخرجات AOT آلة Dart الافتراضية مبسطة + كود أصلي مترجم. استخدم strip لإزالة رموز التصحيح و tree shaking لإزالة الكود غير المستخدم. بناء AOT نموذجي لـ Dart CLI هو 10-20 ميجابايت.

س: هل تكلفة إنشاء Isolate كبيرة؟ ج: يستغرق Isolate.spawn ~50-100 مللي ثانية، و Isolate.run ~30 مللي ثانية. للمهام طويلة الأمد، استخدم Isolate Pool لإعادة استخدام العمال وتجنب الإنشاء/التدمير المتكرر.

س: كيف يعمل ضغط Stream العكسي مع Isolates معاً؟ ج: يقرأ الـ Isolate الرئيسي Stream ويخزنه مؤقتاً بحجم ثابت، ويوقف عند الامتلاء. تستهلك Isolates العامل البيانات المخزنة مؤقتاً وتُخبر الـ Isolate الرئيسي للاستئناف عند الانتهاء. إنه مشابه لنمط المنتج-المستهلك.

س: كيف يمكنني تحسين نقاط pub.dev الخاصة بي؟ ج: تأكد من: 1) pubspec.yaml كامل (homepage/repository/description)؛ 2) README.md مفصل؛ 3) dart analyze بدون تحذيرات؛ 4) تغطية الاختبار > 80%؛ 5) تعليقات الوثائق تغطي جميع واجهات API العامة.

س: كيف أخزن التبعيات مؤقتاً في GitHub Actions؟ ج: استخدم actions/cache لتخزين ~/.pub-cache مؤقتاً، مع المفتاح بناءً على hash لـ pubspec.lock. التشغيل الأول يستغرق ~30 ثانية، إصابة ذاكرة التخزين المؤقت تستغرق ~3 ثوانٍ.

س: كيف أختبر الثنائي المجمع AOT في CI؟ ج: بعد dart compile exe، شغّل ./build/data_pipeline analyze --input test.csv مباشرة، مع التحقق من كود الخروج والمخرجات. هذا أقرب لبيئة الإنتاج من dart run.


📖 ملخص


📝 تمارين

  1. أساسي (صعوبة ⭐): أضف Makefile/سكربت لـ dart compile exe إلى خط أنابيب البيانات، مما يتيح تجميع بناء الإصدار بنقرة واحدة وإخراج حجم الملف.
  2. متوسط (صعوبة ⭐⭐): نفّذ آلية الإيقاف/الاستئناف لضغط Stream العكسي. تحقق بمعالجة بيانات 10 آلاف طلب: الذاكرة لا تزداد أثناء الإيقاف، وتستأنف المعالجة بشكل صحيح.
  3. تحدي (صعوبة ⭐⭐⭐): كون خط أنابيب GitHub Actions CI كامل: فحص التنسيق + التحليل الثابت + اختبار الوحدة + تجميع AOT + اختبار التكامل. ادفع إلى GitHub للتحقق من أن خط الأنابيب يمر.

← الدرس السابق | اكتملت الدورة — تهانينا على إنهاء جميع الدروس الـ 24!

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%