Using abstract vs interface in DAO like IMenuDAO?
Answer
Overview
In Dart, and textabstract class (via textinterface
text
abstract classtext
implementstext
interfaceAbstract 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 | text |
|---|---|---|
| Can have implementation | ✅ Yes | ❌ No (must override all) |
| Multiple inheritance | ❌ One text | ✅ Multiple text |
| Instantiable | ❌ No | ❌ No (abstract) |
| Keyword | text | text |
| Use case | Base class with shared logic | Pure 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
ortextIMenuDAO. Implement separately astextMenuRepository. Inject via abstraction to enable easy mocking and swapping (SQLite ↔ Firestore).textMenuDAOImpl