Question #129MediumGeneral

Using abstract vs interface in DAO like IMenuDAO?

Answer

Overview

In Dart,

text
abstract class
and
text
interface
(via
text
abstract class
+
text
implements
) are used to define contracts for DAO (Data Access Object) patterns. Dart has no
text
interface
keyword — interfaces are defined using abstract classes.


Abstract Class for DAO

dart
// IMenuDAO — interface as abstract class
abstract class IMenuDAO {
  Future<List<MenuItem>> getAllMenuItems();
  Future<MenuItem?> getMenuById(int id);
  Future<void> insertMenuItem(MenuItem item);
  Future<void> updateMenuItem(MenuItem item);
  Future<void> deleteMenuItem(int id);
}

// Concrete implementation
class MenuDAOImpl implements IMenuDAO {
  final Database _db;
  MenuDAOImpl(this._db);

  
  Future<List<MenuItem>> getAllMenuItems() async {
    final result = await _db.query('menu_items');
    return result.map((e) => MenuItem.fromJson(e)).toList();
  }

  
  Future<MenuItem?> getMenuById(int id) async {
    final result = await _db.query(
      'menu_items', where: 'id = ?', whereArgs: [id],
    );
    return result.isEmpty ? null : MenuItem.fromJson(result.first);
  }

  
  Future<void> insertMenuItem(MenuItem item) async {
    await _db.insert('menu_items', item.toJson());
  }

  
  Future<void> updateMenuItem(MenuItem item) async {
    await _db.update('menu_items', item.toJson(), where: 'id = ?', whereArgs: [item.id]);
  }

  
  Future<void> deleteMenuItem(int id) async {
    await _db.delete('menu_items', where: 'id = ?', whereArgs: [id]);
  }
}

Abstract Class (with shared implementation) vs Interface

dart
// Abstract class — can have shared implementation
abstract class BaseDAO<T> {
  // Concrete method — shared across all DAOs
  Future<List<T>> getAll();

  // Abstract method — must be overridden
  Future<T?> getById(int id);

  // Template method pattern
  Future<bool> exists(int id) async {
    return (await getById(id)) != null;
  }
}

// Interface (pure — no implementation)
abstract class IMenuDAO {
  Future<List<MenuItem>> getAllMenuItems(); // No implementation
  Future<MenuItem?> getMenuById(int id);
}

Key Differences

Feature
text
abstract class
text
implements
(Interface)
Can have implementation✅ Yes❌ No (must override all)
Multiple inheritance❌ One
text
extends
only
✅ Multiple
text
implements
Instantiable❌ No❌ No (abstract)
Keyword
text
extends
text
implements
Use caseBase class with shared logicPure contract/interface

Naming Convention for DAO Interfaces

dart
// Prefix with 'I' for interface (common convention)
abstract class IMenuDAO { ... }       // Interface
class MenuDAOImpl implements IMenuDAO { ... } // Implementation
class MockMenuDAO implements IMenuDAO { ... } // Mock for tests

// Or suffix with 'Repository'
abstract class MenuRepository { ... }
class MenuRepositoryImpl implements MenuRepository { ... }

Dependency Injection with DAO

dart
// Service depends on abstract, not concrete
class MenuService {
  final IMenuDAO menuDAO; // ← Interface, not implementation
  MenuService(this.menuDAO);

  Future<List<MenuItem>> getActiveMenus() async {
    final all = await menuDAO.getAllMenuItems();
    return all.where((item) => item.isActive).toList();
  }
}

// Wire up in DI
final service = MenuService(MenuDAOImpl(database));        // Production
final testService = MenuService(MockMenuDAO());            // Testing

Best Practice: Define DAOs as abstract classes (interfaces). Name them

text
IMenuDAO
or
text
MenuRepository
. Implement separately as
text
MenuDAOImpl
. Inject via abstraction to enable easy mocking and swapping (SQLite ↔ Firestore).