What is an Interface in OOP? Example in ActionScript 3

This is written for ActionScript 3 but also works for most similar languages. If you can read the code examples, it will probably apply to you.
In Object-Oriented Programming languages – including AS3, there are special “classes” called Interfaces (it is also a keyword in many languages). You might have noticed some of them in the basic library included with the language. They’re easy to identify because the name always starts with an uppercase I; like IEventDispatcher.

To understand what an Interface class is, you must first know understand what is meant when we talk about an interface of a object.

Coffee machineLet’s say you walk out of your office into the breakroom and find the simplest coffee maker in the world. It only has one button labeled [COFFEE]. You press it and it dispenses a cup of hot and tasty coffee for you. To you as a user the interface of the coffee maker is that button. You only want a cup of coffee, you don’t want to think about what goes on inside every time you use it.

Another simple interface would be a e-reader with just one book in it. It only has two actions you can use on it, go one page left or go one page right. It still has to form the pages on the LCD screen and store and decode the content of the book, but you only see two points of interaction with it. Left and right. If the users could also access the algorithm to render the text to the screen or change the voltage from the battery, they would be very confused and probably break it soon enough.

You sit back to your desk and decide to write your own coffee maker in ActionScript. The pseudo-code for the first revision of your coffee maker class looks like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.designoidgames.interfacetut
{
    public class CoffeeMaker
    {
        public function CoffeeMaker()
        {
                       // Constructor in ActionScript
        }
 
        public function makeCoffee():void
        {
            grindBeans();
            heatWater();
            runWaterThroughBeans();
            coffeeComplete();
        }
 
    }
}
package com.designoidgames.interfacetut
{
	public class CoffeeMaker
	{
		public function CoffeeMaker()
		{
                       // Constructor in ActionScript
		}

		public function makeCoffee():void
		{
			grindBeans();
			heatWater();
			runWaterThroughBeans();
			coffeeComplete();
		}

	}
}

Let’s fill in the details inside the coffee maker with some more pseudo-code so that it actually starts producing coffee for us.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.designoidgames.interfacetut
{
    public class CoffeeMaker
    {
        public function CoffeeMaker()
        {
        }
 
        public function makeCoffee():void
        {
            grindBeans();
            heatWater();
            runWaterThroughBeans();
            coffeeComplete();
        }
 
        private function grindBeans():void
        {
            trace('grind grind grind');
        }
 
        private function heatWater():void
        {
            trace('bubble bubble bubble');
        }
 
        private function runWaterThroughBeans():void
        {
            trace('smells good!');
        }
 
        private function coffeeComplete():void
        {
            trace('coffee ready!');
        }
    }
}
package com.designoidgames.interfacetut
{
	public class CoffeeMaker
	{
		public function CoffeeMaker()
		{
		}

		public function makeCoffee():void
		{
			grindBeans();
			heatWater();
			runWaterThroughBeans();
			coffeeComplete();
		}

		private function grindBeans():void
		{
			trace('grind grind grind');
		}

		private function heatWater():void
		{
			trace('bubble bubble bubble');
		}

		private function runWaterThroughBeans():void
		{
			trace('smells good!');
		}

		private function coffeeComplete():void
		{
			trace('coffee ready!');
		}
	}
}
  • Method means a function that’s part of a class.

Notice that there are two types of methods here, ones that start with public and others that start with the declaration private. The best way to see the difference is to take one step back from the internals of the coffee maker. Let’s go to another class and try to create a new coffee maker instance with what we’ve got so far.

All the details are hidden inside the machine! Private properties and methods are not visible outside of the class. The interface of the coffee maker is now just makeCoffee(). Very user-friendly, just like the one we took inspiration from! It’s easy to imagine that anyone who would come upon this coffee maker class would without a doubt know how to use it based on this interface.

While you’re out to grab a coffee you see a bagel machine and get the craving for one. This one only has one button labeled [BAGEL]. You press the button and after some whirring, a fresh bagel comes out! Taking the bagel maker apart, you peek inside and it seems to work in some ways kind of like the coffee maker… You head back to your desk and program a BagelToaster class based on your research.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.designoidgames.interfacetut
{
    public class BagelToaster
    {
        public function BagelToaster()
        {
        }
 
        public function bakeBagel():void
        {
            addPlainBagel();
            toast();
            spreadCreamCheese();
            bagelComplete();
        }
 
        private function addPlainBagel():void
        {
            trace('added a plain bagel');
        }
 
        private function toast():void
        {
            trace('toasting...');
        }
 
        private function spreadCreamCheese():void
        {
            trace('spreading cream cheese...');
        }
 
        private function bagelComplete():void
        {
            trace('bagel ready!');
        }
    }
}
package com.designoidgames.interfacetut
{
	public class BagelToaster
	{
		public function BagelToaster()
		{
		}

		public function bakeBagel():void
		{
			addPlainBagel();
			toast();
			spreadCreamCheese();
			bagelComplete();
		}

		private function addPlainBagel():void
		{
			trace('added a plain bagel');
		}

		private function toast():void
		{
			trace('toasting...');
		}

		private function spreadCreamCheese():void
		{
			trace('spreading cream cheese...');
		}

		private function bagelComplete():void
		{
			trace('bagel ready!');
		}
	}
}

Sure enough, there’s only one method visible outside, bakeBagel(). Hiding the internal workings of an object is called encapsulation.

You write a few more machines you can think of; CupDispenser that drops out one cup and ElectronicTrashCan which processes your used cups and napkins.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.designoidgames.interfacetut
{
    public class CupDispenser
    {
        public function CupDispenser()
        {
        }
 
        public function dispenseCup():void
        {
            trace('dispensing...');
        }
    }
}
package com.designoidgames.interfacetut
{
	public class CupDispenser
	{
		public function CupDispenser()
		{
		}

		public function dispenseCup():void
		{
			trace('dispensing...');
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.designoidgames.interfacetut
{
    public class ElectronicTrashCan
    {
        public function ElectronicTrashCan()
        {
        }
 
        public function disposeTrash():void
        {
            trace('disposing');
        }
    }
}
package com.designoidgames.interfacetut
{
	public class ElectronicTrashCan
	{
		public function ElectronicTrashCan()
		{
		}

		public function disposeTrash():void
		{
			trace('disposing');
		}
	}
}

Now we have four different machines existing in our codebase. CoffeeMaker, BagelToaster, CupDispenser and ElectronicTrashCan. We want to build a little coffee place simulator using these machines. They all do just one thing visible to the user, prepare (or dispose) one item.

Let’s set up a little cafe with our machines. It’s gonna be called CafeJava. In CafeJava our customers walk in, give their order (an Array of machine instances) and want you to get all the machines running and bring them their order.

The first customer walks in and says [cupDispenser, coffeeMaker]. While you’re writing this down, another runs in and asks for a [bagelToaster, coffeeMaker, electronicTrashCan].

Here is our CafeJava, now how on earth are we going to process all these orders?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.designoidgames.interfacetut
{
    public class CafeJava
    {
 
        /**
         * 
         * @param order List of items the customer wants
         * [cupDispenser, coffeeMaker]
         * [bagelToaster, coffeeMaker, electronicTrashCan]
         */
        public function processOrder(order:Array):void
        {
 
        }
    }
}
package com.designoidgames.interfacetut
{
	public class CafeJava
	{

		/**
		 * 
		 * @param order List of items the customer wants
		 * [cupDispenser, coffeeMaker]
		 * [bagelToaster, coffeeMaker, electronicTrashCan]
		 */
		public function processOrder(order:Array):void
		{

		}
	}
}

Option 1: Write a for loop to traverse through the order array and inside it a big if or switch clause for all of our machines. if(order[i] == cupDispenser) cupDispenser.dispenseCup();
Problem: If we want to add 10 new machines next week, we’re always going to have to manually remember to add them to the clause and other places.

Option 2: We can write a base class that all the machines Inherit from! Easy! This will have the method start() which every machine will override.
Problem: This is the usual approach to a problem in OOP. The problem is that they actually share very little logic, a trash can and a coffee maker. What if in the future the trash can must be able to call for help when it is full and the coffee machine needs to be refilled? We would either have to add all the logic in the base class and have it shared to objects that don’t need it – which would make our objects harder to understand. Why does the coffee machine have a method called dispenseNapkin? Or create a new level of inheritance where we could end up building a chain like BaseMachine -> CallForHelpMachine -> Trash and BaseMachine -> RefillMachine -> CoffeeMaker. But what if we want a Water machine that can both call for cleaning help and refill itself?

Option 3: We could write a Interface class that all our machines can implement. When you implement an Interface the object becomes that Interface in the eye of the code. By sharing a Interface we can treat them all interchangeably. This way we only have to train our cafe employees to use one interface!

Bonus Option 4: Build our program so that a Coffemaker HAS-A RefillService instead of IS-A RefillMachine. But this is about interfaces!

Interface

Let’s call our Interface IMachine (Interface for Machine).

1
2
3
4
5
6
7
package com.designoidgames.interfacetut
{
    public interface IMachine
    {
        function start():void;
    }
}
package com.designoidgames.interfacetut
{
	public interface IMachine
	{
		function start():void;
	}
}

Interface only tells us what it looks like and doesn’t have implementation code.

Let’s add this Interface to our coffee maker.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CoffeeMaker implements IMachine
    {
        public function CoffeeMaker()
        {
        }
        
        public function start():void
        {
            makeCoffee();
        }
        
        ...
        
    }
public class CoffeeMaker implements IMachine
	{
		public function CoffeeMaker()
		{
		}
		
		public function start():void
		{
			makeCoffee();
		}
		
		...
		
	}

Looks simple… let’s do the same for each of our machines. Now if we look back at CafeJava, we can implement the order processing with the new Interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
         * 
         * @param order List of items the customer wants
         * [cupDispenser, coffeeMaker]
         * [bagelToaster, coffeeMaker, electronicTrashCan]
         */
        public function processOrder(order:Array):void
        {
            for each(var machine:IMachine in order)
            {
                machine.start();
            }
        }
/**
		 * 
		 * @param order List of items the customer wants
		 * [cupDispenser, coffeeMaker]
		 * [bagelToaster, coffeeMaker, electronicTrashCan]
		 */
		public function processOrder(order:Array):void
		{
			for each(var machine:IMachine in order)
			{
				machine.start();
			}
		}

As you can see, we are calling start() on an IMachine, not a CoffeeMaker or a BagelToaster. This is called polymorphism. Our code doesn’t need to know exactly what machine it is calling, just that it has the method start() in it.

Our Interface allows us a lot of flexibility now. Imagine receiving a huge legacy Fancy-Coffee-Maker-2000 which already has 5 levels of Inheritance. As an added bonus the base class for the machine extends Array and it needs functionality from there to work. You can’t edit the core library so your only solution with Inheritance would be to hack together a useless layer on top of Array.
But if you just implement the IMachine interface to your new FCM-2000, you can drop it in to your cafe and have it working immediately.