Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Flutter

DART2 Prima Plus - Tutorial 4 - OOPS

5.00/5 (1 vote)
31 Jul 2018CPOL5 min read 9.9K   28  
Object Oriented Programming in DART

Introduction

In this article, I would be discussing Object oriented Programming feature supported by DART language, though it’s not elaborate as current or older market shaker languages like C++, C#. However, it has given enough toolsets/concept to provide programmer comfort while programming.

Background

Almost every programmer agrees with the fact that OOPs is made of these four principals, I will explore this with respect to DART language:

  • Abstraction: DART supports only two types of data access, public and private, no good old protected here and to define any function or variable private, use _ (underscore)
  • Encapsulation: In this principal, DART is similar to rest of the language, it also provides shell (class) to capsule data and business logic.
  • Polymorphism: Polymorphism is divided into two concepts:
    • Compiletime polymorphism: DART doesn't support function overloading, though it supports operator overloading
    • Runtime polymorphism: DART supports function overriding using @override keyword
  • Inheritance - DART supports only single or multi-level inheritance using extends keyword. Though it doesn't support multiple inheritance, you can implement multiple class into one using implements keyword

I would discuss each point with some coding, let our journey begin.

Using the Code

Task#1: Abstraction and Encapsulation

As mentioned earlier, Abstraction has two types of data access, which are private and public. By default, every function and variable is public in DART Class, in comparison to other popular OOPs language like C# and C++, every item is private by default. Reason I believe might be it's closer to javascript.

Secondly, there is no protected keyword in DART, here is small program demonstrating Encapsulation and Abstraction, here I am making class variable as private, please see to make variable private I have used _(underscore) to make variable private. For Encapsulation, we are creating class, which provides capsule or shell to hold variable and methods together and limit the scope of variable till the lifetime of class object.

C#
class AbstractionEncapsulation {

String title;

AbstractionEncapsulation(String argtitle) : title = argtitle {
print("in default constructor $title");
}

AbstractionEncapsulation.fromAnotherObject(
AbstractionEncapsulation abstEncap) {
this.title = abstEncap.title;
}

printTitle() {
print(title);
}

}

void testabstractionencapsulation() {

print("testabstractionencapsulation start");

AbstractionEncapsulation abstractionEncapsulation =
new AbstractionEncapsulation('hello');

AbstractionEncapsulation abstractionEncapsulation1 =
new AbstractionEncapsulation.fromAnotherObject(abstractionEncapsulation);

abstractionEncapsulation1.printTitle();
}

Image 1

Constructor/Destructor

There is no concept of destructor in DART, as it is a garbage collected language, whenever class object holds no reference, it would be removed from memory. Since there is no concept weak references (till the time of writing this article) or pointer in DART. This DART runtime manages removal of object memory, when it's not referenced by any other object.

You can have only one default/parameterised constructor in DART class, rest should be named one as DART doesn't support method overloading, so name of function should be different, otherwise you would get compile time error, reason for majority of time DART programs are fashioned to alternate to JavaScript, so it took lot of concepts from there. Since it's still maturing, hopefully it supports function overloading in future.

Task#2: Inheritance

Let me explain Inheritance in DART first before describing polymorphism. As for overriding or runtime polymorphism, we require Inheritance. Now as mentioned earlier, DART supports single and Multi-Level inheritance using extends, Though Multiple Inheritance is not supported, you can implement multiple class into new class using implements keyword. Main difference between extending and implementing is that when you extend, you will extending the class functionality (the main feature of Inheritance) however when you are implementing, you are composing class from one or more class. From DART website:

Quote:

Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B’s API without inheriting B’s implementation, class A should implement the B interface.

Let's check how to do single inheritance in DART:

C#
abstract class Shape {

String get name;
set length(int ilen);
set breath(int ibre);
num getArea();
}

class Rectangle extends Shape {

int _length, _breath;

@override
set breath(int ibre) {
_breath = ibre;
}

@override
num getArea() {
return _length * _breath;
}

@override
set length(int ilen) {
_length = ilen;
}

@override
String get name => 'Rectangles';
}

testsingleinheritencedart() {

singleinheritencedart.Shape sh = new singleinheritencedart.Rectangle();
sh.breath = 10;
sh.length = 10;

print("the area of ${sh.name} is ${sh.getArea()}");

}

Image 2

This code will showcase multiple inheritance in DART, here I am creating class circle by implementing both Shape class from the previous example and ShapePerimeter:

C#
import 'package:dart4_oops/singleinheritence.dart';

abstract class ShapePerimeter {
  num getPerimeter();
}

class Circle implements ShapePerimeter, Shape {
  int _length;
  @override
  set breath(int ibre) {
    // TODO: implement breath
  }

  @override
  num getArea() {
    return (3.14 * _length * _length);
  }

  @override
  num getPerimeter() {
    return (2 * 3.14 * _length);
  }

  @override
  set length(int ilen) {
    _length = ilen;
  }

  // TODO: implement name
  @override
  String get name => 'Circle';
}

testmultipleinheritencedart() {
  singleinheritencedart.Shape sh = new multipleinheritencedart.Circle();

  sh.length = 10;

  multipleinheritencedart.ShapePerimeter sp =
      sh as multipleinheritencedart.Circle;

  print("the area of ${sh.name} is ${sh.getArea()}");
  print("the perimeter of ${sh.name} is ${sp.getPerimeter()}");
}

Image 3

Task#3: Polymorphism

Now till this point, you are aware how Abstraction, Encapsulation and Inheritance have been implemented in DART, now let's learn the last principle of OOPs now. Polymorphism whose literal meaning is to be existing in multiple forms. DART polymorphism is not as extensive, however there is lot of workaround to achieve similar results. First of all, method overloading is not supported, i.e.:

Following code wouldn't be compiled in DART, which is a very common scenario in C++/ C# to demonstrate method overloading:

C#
void Overloading1(int a){}

void Overloading1(int a,int b){}

However, similar functionality could be achieved using named and optional parameters (though it makes function bloat and with against SOLID principle). C# supports both named and optional parameters, and C++ support only later. For optional parameter, we use [] (Square bracket) and named parameter is specified in {} (curly bracket).

Using optional parameter - Using named parameter

C#
class Overloading {
  final int Price;
  Overloading({this.Price});
//using Optional Parameter
  int getTyrePrice(int iTyres, [int price]) {
    if (price != null) return iTyres * price;
    return iTyres * Price;
  }

//-- Using Named Parameter 
  String getTyreBrand(int price, {int multiple}) {
    int iprice = price;
    if (multiple != null) iprice *= multiple;

    if (iprice < 100)
      return "APOLLO TYRES";
    else if (iprice < 200)
      return "BRIDGESTONE ";
    else if (iprice < 500) return "MICHELIN ";
    return "Tyre not available";
  }
}

testOverloadingdart() {
  Overloading obj = new Overloading(Price: 100);
  print(obj.getTyrePrice(5));
  print(obj.getTyrePrice(5, 200));
  print(obj.getTyreBrand(150));
  print(obj.getTyreBrand(90, multiple: 3));
}

Image 4

However, don't get disheartened if there is no support for Function overloading, however DART supports operator overloading. Here is a list of overridable operator from DART website.

Overridable operators

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>  

Let's do some coding to demonstrate the same, here I am overloading + operator.

C#
class OperatorOverload {
  int value = 0;

  operator +(int i) {
    this.value += i;
  }
}

void testOperatorOverload() {
  OperatorOverload oo = new OperatorOverload();
  print("initial value ${oo.value}");
  oo.value += 10;
  print("new value ${oo.value}");
}

Image 5

With this, compile time polymorphism is complete, let's dive into runtime polymorphism using inheritance and overriding.

Bonus: Factory Constructor

This is a unique type of constructor introduced in DART. Firstly, factory keyword would be applied only on constructor. You can use this to create Singleton and Factory pattern based class. Here, let's dive straight into the code to understand it properly.

Factory Constructor based Singleton pattern: Though it's not fulfilling number 1 condition of Singleton pattern, which is making constructor private, however I am fullfilling the second condition of control over object creation.

C#
class Singleton {
  static Singleton _objSingleton;
  String _information;

  String get Information => _information;
  set Information(String info) {
    _information = info;
  }

  Singleton() {}

  factory Singleton.Me() {
    if (_objSingleton == null) _objSingleton = new Singleton();
    return _objSingleton;
  }
}

void testSingleton() {
  Singleton singleton = new Singleton.Me();
  singleton.Information = "Information from first Object";
  print("From singleton object :" + singleton.Information);

  Singleton singleton1 = new Singleton.Me();
  print("From singleton1 object :" + singleton1.Information);
}

Image 6

Note: Dart currently supports single thread only, so there is no concept of locking or Mutex in DART, which is very common sighting in C#/C++ code when you are creating singleton object dynamically, where thread synchronization code is added just to ensure that object is created only once.

Factory Constructor based Factory Pattern: I utilize abstract class and using factory constructor, I am making sure the type of object I need:

C#
enum EAnimal { Lion, Cat }

abstract class Animal {
  Animal() {}
  factory Animal.GetAnimal(EAnimal animal) {
    switch (animal) {
      case EAnimal.Lion:
        return new Loin();
      case EAnimal.Cat:
        return new Cat();
      default:
        return null;
    }
  }

  String SayHello();
}

class Cat extends Animal {
  @override
  String SayHello() {
    return "Cat say meow";
  }
}

class Loin extends Animal {
  @override
  String SayHello() {
    return "Loin Roar";
  }
}

void testFactoryPattern() {
  print(new Animal.GetAnimal(EAnimal.Lion).SayHello());
  print(new Animal.GetAnimal(EAnimal.Cat).SayHello());
}

Image 7

With this, I have reached the end of this tutorial. Thanks for reading, please do share your comments. You can reach me by twitter too, my handle is @thatsalok.

Points of Interest

Flutter Tutorial

  1. Flutter Getting Started: Tutorial #1
  2. Flutter Getting Started: Tutorial 2 - StateFulWidget
  3. Flutter Getting Started: Tutorial 3 Navigation
  4. Flutter Getting Started: Tutorial 4 ListView
  5. Flutter Getting Started: Tutorial 5 Grid

DART Series

  1. DART2 Prima Plus - Tutorial 1
  2. DART2 Prima Plus - Tutorial 2 - LIST
  3. DART2 Prima Plus - Tutorial 3 - MAP

History

  • 31-July-2018 - First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)