Summary: In this tutorial, you’ll learn how to use the Dart interface class to create contracts that other classes must adhere to.
What is an interface class? #
In Dart, an interface is a contract between classes. Additionally, every class is an implicit interface.
Dart 3 introduced an interface
modifier that allows you to define a contract between classes.
When you define a class with the interface
modifier, you create a contract that other classes must implement, but cannot extend.
Here’s the basic syntax for defining an interface class:
abstract interface class InterfaceName {
methodName1();
methodName2();
}
Code language: PHP (php)
In this syntax:
- First, specify the interface name after the
interface class
keywords. If you don’t provide implementations for methods in the interface, you need to mark it asabstract
. - Second, define methods for interfaces. These methods may have implementation or not, depending on whether
abstract
modifier is available.
If a class wants to use the InterfaceName
, it must implement all the methods of the interface, including methodName1
and methodName2
.
For example, the following defines an interface called Logger
:
abstract interface class Logger {
void log(String message);
}
class ConsoleLogger implements Logger {
@override
void log(String message) {
print('Console: $message');
}
}
void main() {
Logger logger = ConsoleLogger();
logger.log('Hi');
}
Code language: JavaScript (javascript)
Output:
Console: Hi
Code language: HTTP (http)
Interface with Method Implementation #
The following example redefines the Logger
interface with method implementation:
interface class Logger {
void log(String message) {
print('Default log: $message');
}
}
class FileLogger implements Logger {
@override
void log(String message) {
print('Writing to file: $message'); // Must still override
}
}
void main() {
Logger logger = FileLogger();
logger.log('Hi');
}
Code language: JavaScript (javascript)
Output:
Writing to file: Hi
How it works.
First, define the Logger
interface that has a method log()
with an implementation:
interface class Logger {
void log(String message) {
print('Default log: $message');
}
}
Code language: JavaScript (javascript)
Second, define the FileLogger
class that implements the Logger
interface:
class FileLogger implements Logger {
@override
void log(String message) {
print('Writing to file: $message'); // Must still override
}
}
Code language: PHP (php)
Third, create a FileLogger
instance and call the log()
method in the main()
function:
void main() {
Logger logger = FileLogger();
logger.log('Hi');
}
Code language: JavaScript (javascript)
Dart Interface class example #
Let’s model a payment system using interface class
:
abstract interface class PaymentGateway {
Future<void> charge(double amount);
}
class StripePayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using Stripe...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}
class PayPalPayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using PayPal...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}
enum Gateway {
stripe,
paypal
}
class PaymentGatewayFactory {
static PaymentGateway create(Gateway name) {
switch(name) {
case Gateway.stripe: return StripePayment();
case Gateway.paypal: return PayPalPayment();
}
}
}
void main() {
var pg = PaymentGatewayFactory.create(Gateway.stripe);
pg.charge(10.0);
}
Code language: PHP (php)
How it works.
Step 1. Define a PaymentGateway
interface with one method charge()
:
abstract interface class PaymentGateway {
Future<void> charge(double amount);
}
Code language: JavaScript (javascript)
Step 2. Implement the PaymentGateway
using Stripe API (simulated only):
class StripePayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using Stripe...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}
Code language: PHP (php)
Step 3. Implement the PaymentGateway
using PayPal API (simulated only):
class PayPalPayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using PayPal...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}
Code language: PHP (php)
Step 4. Define an enum that includes the payment gateway name:
enum Gateway {
stripe,
paypal
}
Step 5. Define a class that creates an instance of a class that implements the PaymentGateway
interface:
class PaymentGatewayFactory {
static PaymentGateway create(Gateway name) {
switch(name) {
case Gateway.stripe: return StripePayment();
case Gateway.paypal: return PayPalPayment();
}
}
}
Code language: JavaScript (javascript)
Step 6. Create a PaymentGateway
instance and call the charge()
method:
void main() {
var pg = PaymentGatewayFactory.create(Gateway.stripe);
pg.charge(10.0);
}
Code language: JavaScript (javascript)
In practice, you can read the gateway name from a configuration file and dynamically swap between the payment gateways.
If you want to add payment gateway, you can:
- First, define a class that implements the PaymentGateway interface.
- Second, add the payment gateway name to the enum.
- Third, swap it in the main() method. If you use a configuration, you don’t need to change the main() method.
Summary #
- Use an interface class to define a contract between classes.
- Classes can implement an interface class, but cannot extend it.