Flutter Design Patterns: 4— Builder

Omer Shafique
5 min readNov 2, 2022

--

In this article, we’ll discuss... Builder pattern was introduced to solve some of the problems with Factory and Abstract Factory design patterns.

Why need a Builder Pattern?

What Is a Builder Pattern?

Advantages/Disadvantages of Builder Pattern.

How To Implement a Factory Method In Dart:

Conclusion

Why need a Builder Pattern?

There are three major issues with Factory and Abstract Factory design patterns when the Object contains a lot of attributes.

  1. Too Many arguments to pass from the client program to the Factory class can be error-prone because most of the time, the type of arguments are the same and from the client side it's hard to maintain the order of the argument.
  2. Some of the parameters might be optional but in the Factory pattern, we are forced to send all the parameters and optional parameters need to send as NULL.
  3. If the object is heavy and its creation is complex, then all that complexity will be part of Factory classes that is confusing.

We can solve the issues with a large number of parameters by providing a constructor with the required parameters and then different setter methods to set the optional parameters. The problem with this approach is that the Object state will be inconsistent unless all the attributes are set explicitly. Builder pattern solves the issue with a large number of optional parameters and inconsistent state by providing a way to build the object step-by-step and provide a method that will return the final Object.

What Is Builder Pattern?

Builder pattern aims to “Separate the construction of a complex object from its representation so that the same construction process can create different representations.”

Construct a complex object from simple objects using step-by-step approach

It is mostly used when an object can’t be created in a single step like in the de-serialization of a complex object.

This type of design pattern comes under creational patterns as this pattern provides one of the best ways to create an object.

Advantages of Builder Design Pattern

  • The parameters of the constructor are reduced and are provided in highly readable method calls.
  • Builder design pattern also helps in minimizing the number of parameters in the constructor and thus there is no need to pass in null for optional parameters to the constructor.
  • An object is always instantiated in a complete state
  • Immutable objects can be built without much complex logic in the object-building process.

Disadvantages of Builder Design Pattern

  • The number of lines of code increases by at least double in the builder pattern, but the effort pays off in terms of design flexibility and much more readable code.
  • Requires creating a separate ConcreteBuilder for each different type of Product.

Implementation

We have considered a business case of a fast-food restaurant where a typical meal could be a burger and a cold drink. The burger could be either a Veg Burger or Chicken Burger and will be packed in a wrapper. The cold drink could be either Coke or Pepsi and will be packed in a bottle.

We are going to create an Item interface representing food items such as burgers and cold drinks and concrete classes implementing the Item interface and a Packing interface representing packaging of food items and concrete classes implementing the Packing interface as burgers would be packed in a wrapper and cold drink would be packed in a bottle.

We then create a Meal class having an ArrayList of Item and a MealBuilder to build different types of Meal objects by combining Item. BuilderPatternDemo, our demo class will use MealBuilder to build a Meal.

  1. Create an interface Item representing food items and packing.
abstract class Item{
String name();
Packing packing();
double price();
}
abstract class Packing{
String pack();
}

2. Create concrete classes implementing the Packing interface.

class Wrapper implements Packing{ 
@Override
String pack(){
return "Wrapper";
}
}
class Bottle implements Packing{
@Override
String pack(){
return "Bottle";
}
}

3. Create abstract classes implementing the Item interface providing default functionalities.

abstract class Burger implements Item{
@Override
Packing packing(){
return new Wrapper();
}

@Override
double price();
}
abstract class ColdDrink implements Item{
@Override
Packing packing(){
return new Bottle();
}

@Override
double price();
}

4. Create concrete classes extending Burger and ColdDrink classes.

VegBurger.dart

class VegBurger extends Burger{
@Override
double price(){
return 25;
}
@Override
String name(){
return "Veg Burger";
}
}

ChickenBurger.dart

class ChickenBurger extends Burger{
@Override
double price(){
return 50;
}
@Override
String name(){
return "Chicken Burger";
}
}

Coke.dart

class Coke extends ColdDrink{
@Override
double price(){
return 30;
}
@Override
String name(){
return "Coke";
}
}

Pepsi.dart

class Pepsi extends ColdDrink{
@Override
double price(){
return 35;
}
@Override
String name(){
return "Pepsi";
}
}

5. Create a Meal class having Item objects defined above.

Meal.dart

class Meal
{
List<Item> items = [Item()];

void addItem(Item item){
items.add(item);
}

double getCost(){
double cost = 0;
for (Item item in items) {
cost += item.price();
}
return cost;
}

void showItems(){
for (Item item in items) {
print("Item : " + item.name());
print(", Packing : " + item.packing().pack());
print(", Price : " + item.price());
}
}
}

6. Create a MealBuilder class, the actual builder class responsible to create Meal objects.

MealBuilder.dart

class MealBuilder{

Meal prepareVegMeal(){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}

Meal prepareNonVegMeal(){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}

7. BuiderPatternDemo uses MealBuider to demonstrate builder pattern.

BuilderPatternDemo.java

class BuilderPatternDemo{    static void main(List<String> args){        MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
print("Veg Meal");
vegMeal.showItems();
print("Total Cost: " + vegMeal.getCost());
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
print("\n\nNon-Veg Meal");
nonVegMeal.showItems();
print("Total Cost: " + nonVegMeal.getCost());
}
}

8. Output

Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0


Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.0

Conclusion:

In the article, I have explained the structure of the Builder Design Pattern for Dart and Flutter; you can modify this code according to your choice.

I hope this blog will provide you with sufficient information on Trying up the Builder Design Patterns for Dart and Flutter in your projects. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I got something wrong? Let me know in the comments 💬. I would love to improve.

Clap 👏 If this article helps you and to show your support and motivates me to write better!

Share 📢 this article with your friends, and colleagues on social media.

Follow ➕ me on Medium.

--

--