Design Patterns thread

Soldato
Joined
1 Mar 2003
Posts
5,508
Location
Cotham, Bristol
Hi All,

I've recently purchased a Pluralsight subscription. One of the tracks I'm currently following is their Design pattern library as it's not something I've particularly touched much of.

Anyway their examples are all .NET which I'm not familiar with so to get to grips I'm looking to create my own simple examples in Java.

First things first was the Adapter pattern, one of the simplest examples I could think of was UK Plug -> US Socket and US Socket -> UK Plug, an adaptor fitting in between both examples.

Here is what I have (please ignore my obvious lack of knowledge in plug wiring :D), is my understanding correct?

USPlug.java
Code:
package com.adapter;

public class USPlug {
  
  private int live;
  private int neutral;
  
  private IAdaptor adaptor;
  
  public USPlug(int live, int neutral) {
    this.live = live;
    this.neutral = neutral;
  }
  
  public int getLive() {
    return live;
  }
  
  public int getNeutral() {
    return neutral;
  }
  
  public void setAdaptor(IAdaptor adaptor) {
    this.adaptor = adaptor;
  }
  
  public String plugin() {
    return adaptor.plugin();
  }

}

UKPlug.java
Code:
package com.adapter;

public class UKPlug {
  
  private int earth;
  private int live;
  private int neutral;
  
  private IAdaptor adaptor;
  
  public UKPlug(int earth, int live, int neutral) {
    this.earth = earth;
    this.live = live;
    this.neutral = neutral;
  }
  
  public String plugin() {
    return adaptor.plugin();
  }

  /**
   * @return the earth
   */
  public int getEarth() {
    return earth;
  }

  /**
   * @return the live
   */
  public int getLive() {
    return live;
  }

  /**
   * @return the neutral
   */
  public int getNeutral() {
    return neutral;
  }

  /**
   * @param adaptor the adaptor to set
   */
  public void setAdaptor(IAdaptor adaptor) {
    this.adaptor = adaptor;
  }

}

USSocket.java
Code:
package com.adapter;

public class USSocket {
  
  public String plugin(int live, int neutral) {
    if(live == 1 && neutral == 1) {
      return "We have Power!";
    } else {
      return "Uh Oh somethings not right, check your wiring!";
    }
  }

}

UKSocket.java
Code:
package com.adapter;

public class UKSocket {  
  
  public String plugin(int earth, int live, int neutral) {
    if(earth == 1 && live == 1 && neutral ==1) {
      return "We have Power!"; 
    } else {
      return "Uh Oh somethings not right, check your wiring!";
    }    
  }

}

IAdaptor.java
Code:
package com.adapter;

public interface IAdaptor {
  
  public String plugin();

  
}

USUKAdaptor.java
Code:
package com.adapter;

public class USUKAdaptor implements IAdaptor {
  
  private USPlug plug;
  private UKSocket socket;
  
  public USUKAdaptor(UKSocket socket) {
    this.socket = socket;
  }

  @Override
  public String plugin() {    
    return socket.plugin(1, plug.getLive(), plug.getNeutral());
  }
  
  public void setPlug(USPlug plug) {
    this.plug = plug;
  }

}

UKUSAdaptor.java
Code:
package com.adapter;

public class UKUSAdaptor implements IAdaptor {
  
  private UKPlug plug;
  private USSocket socket;
  
  public UKUSAdaptor(USSocket socket) {
    this.socket = socket;
  }

  @Override
  public String plugin() {
    return socket.plugin(plug.getLive(), plug.getNeutral());
  }
  
  public void setPlug(UKPlug plug) {
    this.plug = plug;
  }

}

Plugin.java
Code:
package com.adapter;

/**
 * This is the adaptor pattern
 * @author pstatham
 *
 */
public class Plugin {
  
  public static void main(String[] args) {
    //First test the USUKAdaptor
    testUSUKAdaptor();
    
    //Now test the UKUSAdaptor
    testUKUSAdaptor();
  }

  private static void testUSUKAdaptor() {
    System.out.println("Testing US->UK Adaptor");
    USPlug goodUSPlug = new USPlug(1, 1);
    USPlug badUSPlug = new USPlug(0, 1);
    
    UKSocket ukSocket = new UKSocket();
    
    USUKAdaptor usukadaptor = new USUKAdaptor(ukSocket);
    usukadaptor.setPlug(goodUSPlug);
    
    goodUSPlug.setAdaptor(usukadaptor);
    badUSPlug.setAdaptor(usukadaptor);
    
    System.out.println(goodUSPlug.plugin());
    
    usukadaptor.setPlug(badUSPlug);
    
    System.out.println(badUSPlug.plugin());
  }

  private static void testUKUSAdaptor() {
    System.out.println("Testing UK->US Adaptor");
    UKPlug goodUKPlug = new UKPlug(1, 1, 1);
    UKPlug badUKPlug = new UKPlug(1, 0, 1);
    
    USSocket usSocket = new USSocket();
    
    UKUSAdaptor ukusadaptor = new UKUSAdaptor(usSocket);
    ukusadaptor.setPlug(goodUKPlug);
    
    goodUKPlug.setAdaptor(ukusadaptor);
    badUKPlug.setAdaptor(ukusadaptor);
    
    System.out.println(goodUKPlug.plugin());
    
    ukusadaptor.setPlug(badUKPlug);
    
    System.out.println(badUKPlug.plugin());
  }

}
 
Soldato
OP
Joined
1 Mar 2003
Posts
5,508
Location
Cotham, Bristol
Slowly but surely working through these, so far I have covered adapter, bridge, builder, chain of responsibility, command and composite.

The GOF defines the composite as

The composite pattern describes that a group of objects are to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

I took a car and its component parts as an example

ICarComponent.java
Code:
package com.composite;

public interface ICarComponent {
  
  public void printComponentDetails();

}

CarPart.java
Code:
package com.composite;

public class CarPart implements ICarComponent {
  
  private String partName;
  
  public CarPart(String partName) {
    this.partName = partName;
  }

  @Override
  public void printComponentDetails() {
    System.out.println(partName);
  }

}

CarPartGroup.java
Code:
package com.composite;

import java.util.List;

public class CarPartGroup implements ICarComponent {
  
  private List<ICarComponent> componentList;
  
  public CarPartGroup(List<ICarComponent> componentList) {
    this.componentList = componentList;
  }

  @Override
  public void printComponentDetails() {
    for(ICarComponent component: componentList) {
      component.printComponentDetails();
    }
  }

}

Program.java
Code:
package com.composite;

import java.util.Arrays;

/**
 * The composite pattern is a partitioning design pattern. The composite pattern describes
 * that a group of objects are to be treated in the same way as a single instance of an object.
 * The intent of a composite is to "compose" objects into tree structures to represent part-whole
 * hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly
 * @author pstatham
 *
 */
public class Program {
  
  public static void main(String[] args) {
    CarPart frontLeftWheel = new CarPart("Front left wheel");
    CarPart frontRightWheel = new CarPart("Front right wheel");
    CarPart backLeftWheel = new CarPart("Back left wheel");
    CarPart backRightWheel = new CarPart("Back right wheel");
    
    CarPartGroup wheels = new CarPartGroup(Arrays.asList(new ICarComponent[]{frontLeftWheel, frontRightWheel, backLeftWheel, backRightWheel}));
    
    CarPart frontLeftDoor = new CarPart("Front left door");
    CarPart frontRightDoor = new CarPart("Front right door");
    CarPart backLeftDoor = new CarPart("Back left door");
    CarPart backRightDoor = new CarPart("Back right door");
    
    CarPartGroup doors = new CarPartGroup(Arrays.asList(new ICarComponent[]{frontLeftDoor, frontRightDoor, backLeftDoor, backRightDoor}));
    
    CarPart engine = new CarPart("Engine");
    
    CarPartGroup car = new CarPartGroup(Arrays.asList(new ICarComponent[]{wheels, doors, engine}));
    car.printComponentDetails();
  }

}
 
Soldato
Joined
13 Feb 2003
Posts
6,157
I would definitely say your adapter is not correct. Adapter should not be part of the plug class. Think about this - I want to put a UK plug in a UK socket - adapter should not be seen at all.

As a design pattern the things being adapted will be (should be...) totally agnostic of the adapter. The adapter and the adaptee will have different interfaces - this is the whole point of the adapter pattern.

So a uk plug might have interface 'plug_into_3pin()', whilst a us plug would be 'plug_into_2pin()'.

The adapter would take a uk plug, say, and instead present the outward interface 'plug_into_2pin()'. I think you may be confusing the adapter pattern with some aspects of what a plug adapter does.
 
Soldato
OP
Joined
1 Mar 2003
Posts
5,508
Location
Cotham, Bristol
Hi thanks for taking the time to respond, whilst I see what you're saying I'm trying to make sense out of it compared to material I'm finding on the internet. Take the class diagram for example

adapter_pattern_0.PNG


Wouldn't the client be the plug and the adaptee be the socket? From the same source (http://java.dzone.com/articles/design-patterns-uncovered-0) we have this sequence diagram

adapter_sequence.PNG


So the client uses the concrete adapter to call the appropriate adaptee method.

Also from wikipedia

300px-ObjectAdapter.png


So (and please correct me if I'm reading this wrong) in my example, my plug calls its own plugin() method which in turn calls the concrete adapters plugin which calls the adaptee plugin.

In the case of a UK plug for a UK socket you're right an adapter shouldn't be needed (I guess instead a reference to the socket would be needed), but from what I've read above, when it does need an adapter it will need a reference to an adapter?

I may edit this as I continue to think about it :p

Ok well I've created a new version, which will allow UK plugs -> UK socket, US plugs -> US sockets, UK plugs -> US sockets and US plugs -> UK sockets. Except I suspect having the adaptee (Socket) implement the adapter is cheating. :YET ANOTHER EDIT: Edited the code below to something that is more likely, i.e. a plug can either have a reference to a socket or an adapter, and removed the implements IAdapter from the sockets (after all it's unlikely that we can change the adaptee code in real life).

Code:
package com.structural.adapter.adapter2;

interface IAdapter {
  public void plugin();
}

class UKUSAdapter implements IAdapter {
  UKPlug plug;
  USSocket socket;
  public UKUSAdapter(UKPlug plug, USSocket socket) {
    this.plug = plug;
    this.socket = socket;
  }

  @Override
  public void plugin() {
    System.out.print("UKUSAdapter -> ");
    socket.accept_2pin_plug(plug.pin1, plug.pin2);
  }
}

class USUKAdapter implements IAdapter {
  USPlug plug;
  UKSocket socket;
  public USUKAdapter(USPlug plug, UKSocket socket) {
    this.plug = plug; 
    this.socket = socket;
  }

  @Override
  public void plugin() {
    System.out.print("USUKAdapter -> ");
    socket.accept_3pin_plug(plug.pin1, plug.pin2, 1);
  }

}

class USSocket {
  USPlug plug;
  public USSocket(USPlug plug) {
    this.plug = plug;
  }

  public void accept_2pin_plug(int pin1, int pin2) {
    System.out.print("USSocket");
  }
}

class UKSocket {
  UKPlug plug;
  public UKSocket(UKPlug plug) {
    this.plug = plug;
  }
  public void accept_3pin_plug(int pin1, int pin2, int pin3) {
    System.out.print("UKSocket");
  }
}

class UKPlug {   
  int pin1;
  int pin2;
  int pin3;

  IAdapter adapter;
  UKSocket socket;

  public UKPlug(int pin1, int pin2, int pin3) {
    this.pin1 = pin1;
    this.pin2 = pin2;
    this.pin3 = pin3;
  }

  public void plug_into_3pin() {
    System.out.print("UKPlug -> ");
    if(this.socket == null) {
      adapter.plugin(); 
    } else {
     socket.accept_3pin_plug(pin1, pin2, pin3); 
    }    
  }

  public void setAdapter(IAdapter adapter) {
    this.adapter = adapter;
    this.socket = null;    
  }
  
  public void setSocket(UKSocket socket) {
    this.socket = socket;
    this.adapter = null;
  }
}

class USPlug {   
  int pin1;
  int pin2;

  IAdapter adapter;
  USSocket socket;

  public USPlug(int pin1, int pin2) {
    this.pin1 = pin1;
    this.pin2 = pin2;
  }

  public void plug_into_2pin() {
    System.out.print("USPlug -> ");
    if(this.socket == null) {
      adapter.plugin(); 
    } else {
      socket.accept_2pin_plug(pin1, pin2);
    }    
  }

  public void setAdapter(IAdapter adapter) {
    this.adapter = adapter;
    this.socket = null;
  }
  
  public void setSocket(USSocket socket) {
    this.socket = socket;
    this.adapter = null;
  }
}

public class TestPlugAdapters {
  public static void main(String[] args) {
    UKPlug ukPlug = new UKPlug(1,1,1);
    UKSocket ukSocket = new UKSocket(ukPlug);

    USPlug usPlug = new USPlug(1,1);
    USSocket usSocket = new USSocket(usPlug);

    ukPlug.setSocket(ukSocket);
    ukPlug.plug_into_3pin();
    System.out.println();

    usPlug.setSocket(usSocket);
    usPlug.plug_into_2pin();
    System.out.println();

    IAdapter ukusAdapter = new UKUSAdapter(ukPlug, usSocket);
    ukPlug.setAdapter(ukusAdapter);
    ukPlug.plug_into_3pin();
    System.out.println();

    IAdapter usukAdapter = new USUKAdapter(usPlug, ukSocket);
    usPlug.setAdapter(usukAdapter);
    usPlug.plug_into_2pin();
    System.out.println();
  }
}

Above code produces

Code:
UKPlug -> UKSocket
USPlug -> USSocket
UKPlug -> UKUSAdapter -> USSocket
USPlug -> USUKAdapter -> UKSocket
 
Last edited:
Soldato
Joined
13 Feb 2003
Posts
6,157
no, that updated code is not good either. Encapsulation is key here, and the plug should not be doing anything at all with an adapter.

The adapter is the one that should accept a reference to something else.

Your IAdapter doesn't present a change of interface, and this is the whole point of the adapter pattern...

Code:
#include <iostream>

class IInterfaceA
{
public:
  virtual void Foo() = 0;
};

class IInterfaceB
{
public:
  virtual void Bar() = 0;
};

class ThingA : public IInterfaceA
{

public:
  virtual void Foo()
  {
    std::cout << "foo\n";
  }
};

class ThingB : public IInterfaceB
{

public:
  virtual void Bar()
  {
    std::cout << "bar\n";
  }
};


class BtoAAdapter : public IInterfaceA
{
  IInterfaceB* my_b;

public:
  BtoAAdapter(IInterfaceB* b) : my_b(b){}

  virtual void Foo()
  {
    my_b->Bar();
  }
};

class Client
{
public:
  IInterfaceA* my_thing;

  void use_thing()
  {
    my_thing->Foo();
  }
};


int main()
{

  ThingA a;
  ThingB b;

  Client client;

  client.my_thing = &a;

  client.use_thing();

  BtoAAdapter adapter(&b);
  client.my_thing = &adapter;

  client.use_thing();
}

Here is a adapter pattern (please excuse dodgy pointer use...). The client wants to always use interface A. An adapter is made to *wrap* an interface B impl.

Neither of the 'Things' know about the adapter. The client doesn't know about the adapter. The adapter takes one interface and modifies it to fit the other. This is a key aspect that is not present in your design. Your design requires modification in the adaptee to work. Sometimes you don't even have access to the source to make these changes. This is why your design is absolutely wrong.

To convert my code to your example,

IInterfaceA -> "uk socket spec"
+ Foo() -> accept 3 pin plug()

ThingA -> "uk socket"

Client -> uk plug

BToA Adapter -> adapter that lets you plug in a uk plug in a US socket
 
Caporegime
Joined
18 Oct 2002
Posts
32,623
^^^ as above, the adaptee should have no knowledge of the adapter.

And in any case, US plugs to have 3 pins, but they have 120v! :D

This does highlight my dislike of design pattersns. It is great to read about these things but ultimately when you have a programming problem you simply want the best solution to the problem meeting the objectives. Analysis of the problem will lead to developing a solution. The concept of Design patterns seems to be "I have these possible solutions, which one of these fits the problem?" Perhaps none, perhaps a mix, perhaps something unique, perhaps the problem is more fundamental and you shouldn't have architectured some thing that needs an adapter. Perhaps it is cleaner to change other things. Even if the adapter pattern is correct, if that is the best solution and you are trying to code the best solution you would have come up with that anyway, by definition.

Taking a light interest in the design pattern mantra will help you as a developer, but blindly trying to force pre-baked solutions with fancy names to a problem which you read it in a book doesn't help.
 
Last edited:
Soldato
Joined
18 Oct 2002
Posts
3,926
Location
SW London
The concept of Design patterns seems to be "I have these possible solutions, which one of these fits the problem?"

I think if anyone approaches design patterns in that way then they're doing it wrong.

Patterns are really just common, well understood ways of describing certain implementations of functionality.
In a similar way to how you might describe how you want a house building to a builder you could use well know terms that he would understand: gables, windows, rooves etc. then design patterns allow a common understanding of what you're doing.

If you were describing to a fellow developer your thoughts on implementing a particular feature then you could say something like 'well, we could use an iterator to go over the items we care about and then construct what we need with a factory' he/she would immediately understand what you were talking about.

Most of the original GoF patterns are simply what fall out of well structured object oriented programming, though anyone implementing singletons in the way they describe deserves to be shot!

All good developers should at least have an understanding of what common design patterns are, how they're structured and when to use them, but you certainly shouldn't be striving to fit them into your code just for the sake of it, more a case of if you design things well then what you're doing will naturally fall into sets of well described patterns as what you're doing is at least vaguely similar to what many other developers have done before.
 
Soldato
OP
Joined
1 Mar 2003
Posts
5,508
Location
Cotham, Bristol

Many thanks! Sorry for taking some time to reply but I've become a Dad for the second time in the last 8 weeks, so I've been somewhat pre-occupied. I've adapted (geddit?! :p) my adapter to your suggestions, here's an update

Code:
package com.structural.adapter.adapter3;

interface IUKSocket {
  public void accept_3pin_plug(int pin1, int pin2, int pin3);
}

interface IUSSocket {
  public void accept_2pin_plug(int pin1, int pin2);
}

class UKSocket implements IUKSocket {

  @Override
  public void accept_3pin_plug(int pin1, int pin2, int pin3) {
    if(pin1 == 1 && pin2 == 1 && pin3 ==1) {
      System.out.print("We have UKSocket power!\r\n"); 
    } else {
      System.out.print("Uh Oh! Something is wrong with the UKSocket\r\n");
    }    
  }

}

class UKPlug {
  IUKSocket socket;

  int pin1;
  int pin2;
  int pin3;

  public UKPlug(int pin1, int pin2, int pin3) {
    this.pin1 = pin1;
    this.pin2 = pin2;
    this.pin3 = pin3;
  }

  public void plugInToSocket() {
    System.out.print("UKPlug -> ");
    socket.accept_3pin_plug(pin1, pin2, pin3);
  }
}

class USSocket implements IUSSocket {

  @Override
  public void accept_2pin_plug(int pin1, int pin2) {
    if(pin1 == 1 && pin2 == 1) {
      System.out.print("We have USSocket power!\r\n"); 
    } else {
      System.out.print("Uh Oh! Something is wrong with the USSocket\r\n");
    }
  }

}

class USPlug {
  IUSSocket socket;

  int pin1;
  int pin2;

  public USPlug(int pin1, int pin2) {
    this.pin1 = pin1;
    this.pin2 = pin2;
  }

  public void plugInToSocket() {
    System.out.print("USPlug -> ");
    socket.accept_2pin_plug(pin1, pin2);
  }
}

class UStoUKAdapter implements IUSSocket {

  IUKSocket targetUKSocket;

  public UStoUKAdapter(IUKSocket socket) {
    this.targetUKSocket = socket;
  }

  @Override
  public void accept_2pin_plug(int pin1, int pin2) {
    System.out.print("UStoUKAdapter -> ");
    targetUKSocket.accept_3pin_plug(pin1, pin2, 1);    
  }

}

class UKtoUSAdapter implements IUKSocket {

  IUSSocket targetUSSocket;

  public UKtoUSAdapter(IUSSocket socket) {
    this.targetUSSocket = socket;
  }

  @Override
  public void accept_3pin_plug(int pin1, int pin2, int pin3) {
    System.out.print("UKtoUSAdapter -> ");
    targetUSSocket.accept_2pin_plug(pin1, pin2);    
  }

}

public class TestPlugAdapters {

  public static void main(String... strings) {
    UKSocket ukSocket = new UKSocket();
    USSocket usSocket = new USSocket();

    UKPlug ukPlug = new UKPlug(1, 1, 1);
    USPlug usPlug = new USPlug(1, 1);

    ukPlug.socket = ukSocket;
    ukPlug.plugInToSocket();

    IUKSocket uk2usAdapter =  new UKtoUSAdapter(usSocket);

    ukPlug.socket = uk2usAdapter;
    ukPlug.plugInToSocket();
    
    usPlug.socket = usSocket;
    usPlug.plugInToSocket();
    
    IUSSocket us2ukAdapter =  new UStoUKAdapter(ukSocket);
    
    usPlug.socket = us2ukAdapter;
    usPlug.plugInToSocket();
  }

}

^^^ as above, the adaptee should have no knowledge of the adapter.

And in any case, US plugs to have 3 pins, but they have 120v! :D

This does highlight my dislike of design pattersns. It is great to read about these things but ultimately when you have a programming problem you simply want the best solution to the problem meeting the objectives. Analysis of the problem will lead to developing a solution. The concept of Design patterns seems to be "I have these possible solutions, which one of these fits the problem?" Perhaps none, perhaps a mix, perhaps something unique, perhaps the problem is more fundamental and you shouldn't have architectured some thing that needs an adapter. Perhaps it is cleaner to change other things. Even if the adapter pattern is correct, if that is the best solution and you are trying to code the best solution you would have come up with that anyway, by definition.

Taking a light interest in the design pattern mantra will help you as a developer, but blindly trying to force pre-baked solutions with fancy names to a problem which you read it in a book doesn't help.

Thanks noted, I had no intention of blindly trying to apply a pattern to every piece of code I am trying to write. But like you say a knowledge of what they are and what situations I can apply them in will only help.
 
Last edited:
Soldato
OP
Joined
1 Mar 2003
Posts
5,508
Location
Cotham, Bristol
Ok well I'm on to some more generic OOP principles now. Such as Dependency Inversion Principle, Inversion of Control and Dependency Injection.

Now the way I understand it. DIP being a principal doesn't describe how to achieve it so much as what it is which is

- High level modules should not depend on low level modules both should depend on abstractions
- Abstractions should not depend on details. Details should depend on abstractions

As usual that's a little bit abstract without actually seeing examples of what this means. This is where IoC comes in, which from my understanding is the method in which the dependency can be inverted, these methods include

- Interface inversion
- Flow inversion
- Dependency Creation/Binding Inversion, one method of which is Dependency Injection

Now I'm try as usual to create an example of each, so first of all here's what I've come up with for interface inversion

Code:
package com.ioc.interfaceinversion;

/**
 * This code demonstrates Inversion of Control using
 * interface inversion.
 * 
 * Both inverted and non inverted code is shown.
 * 
 * Instead of having the high level consumer object
 * (BoxingMatchNonInverted) depend upon the low level
 * interfaces (IKangarooNonInverted and IMikeTysonNonInverted).
 * 
 * The high level consumer object (BoxingMatch) depends on the 
 * IBoxer abstraction, as do the low level objects (Kangaroo and MikeTyson).
 * 
 * @author pstatham
 *
 */

interface IKangarooNonInverted {
  public void kick();
}

class KangarooNonInverted implements IKangarooNonInverted {

  @Override
  public void kick() {
    System.out.println("Kangaroo Kick!");    
  }

}

interface IMikeTysonNonInverted {
  public void punch();
}

class MikeTysonNonInverted implements IMikeTysonNonInverted {

  @Override
  public void punch() {
    System.out.println("Ooooo punch to the groin!");
  }

}

class BoxingMatchNonInverted {

  private IKangarooNonInverted kangaroo = new KangarooNonInverted();
  private IMikeTysonNonInverted mikeTyson = new MikeTysonNonInverted();

  public void fight(String fighter) {
    if ("kangaroo".equals(fighter)) {
      kangaroo.kick();
    } else {
      mikeTyson.punch();
    }
  }

}

/**
 * This is the 'abstraction'
 * @author pstatham
 *
 */
interface IBoxer {
  public void hit();
}

/**
 * Low level component dependent
 * on high level owned interface
 * @author pstatham
 *
 */
class Kangaroo implements IBoxer {

  @Override
  public void hit() {
    System.out.println("Kangaroo Kick!");
  }

}

/**
 * Low level component dependent
 * on high level owned interface
 * @author pstatham
 *
 */
class MikeTyson implements IBoxer {

  @Override
  public void hit() {
    System.out.println("Ooooo punch to the groin!");
  }

}

/**
 * High level component
 * @author pstatham
 *
 */
class BoxingMatch {
  
  public void fight(IBoxer fighter) {
    fighter.hit();
  }
  
}

public class TestInterfaceInversion {

  public static void main(String[] args) {
    //First test the non inverted way
    BoxingMatchNonInverted match1 = new BoxingMatchNonInverted();
    match1.fight("kangaroo");
    match1.fight("mike");
    
    //Now test with inversion
    BoxingMatch match2 = new BoxingMatch();
    match2.fight(new Kangaroo());
    match2.fight(new MikeTyson());
  }

}

Now the bit I'm a little bit vague on is

Code:
    match2.fight(new Kangaroo());
    match2.fight(new MikeTyson());

Which is obviously an example of dependency injection. Seeing as I'm trying to show interface inversion instead of dependency injection does the code need to change?

And flow inversion

Code:
package com.ioc.flowinversion;

import java.util.Scanner;

/**
 * This is the procedural approach for getting a users name
 * 
 * i.e. 
 * - Ask user for their name
 * - Read their name
 * - Print their name
 * 
 * @author pstatham
 *
 */
public class TestProcedural {
  
  public static void main(String[] args) {    
    Scanner keyboard = new Scanner(System.in);
    System.out.println("Enter you name: ");
    String name = keyboard.next();
    System.out.println("Hello " + name);
    keyboard.close();
  }

}

Code:
package com.ioc.flowinversion;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

/**
 * And this is using the flow inversion approach
 * 
 * i.e. instead of doing
 * - Ask user for their name
 * - Read their name
 * - Print their name
 * 
 * We do 
 * - Wait for user to enter name, store it in a name field
 * - Display message "Hello name" to the text field
 * 
 * @author pstatham
 *
 */
public class TestFlowInversion {
  
  public JComponent makeUI() {
    JPanel panel = new JPanel();
    
    JLabel label = new JLabel("Enter your name: ");
    final JTextField tf = new JTextField(12);
    JButton button = new JButton("OK");
    
    button.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent event) {
        tf.setText("Hello " +tf.getText());        
      }
      
    });
    
    panel.add(label);
    panel.add(tf);
    panel.add(button);
    
    return panel;
  }
  
  public static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new TestFlowInversion().makeUI());
    frame.setSize(400, 400);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
  
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        createAndShowGUI();
      }
    });
  }

}
 
Last edited:
Caporegime
Joined
18 Oct 2002
Posts
29,491
Location
Back in East London
Inversion of Control, to put it simply, is your current object never "news" up anything, except in the case where the object's responsibility is to do so (e.g. a factory).

So instead of:
Code:
public Thing GetMeSomething() {
  SomeOtherThing thing = new SomeOtherThing();
  return thing.Gimme(this.someArbitraryField);
}

you do something like:
Code:
private SomeOtherThing someOtherThing;

public SomeClass(SomeOtherThing someOtherThing) {
  this.someOtherThing = someOtherThing;
}

public Thing GetMeSomething() {
  return this.someOtherThing.Gimme(this.someArbitraryField);
}

or
Code:
public Thing GetMeSomething(SomeOtherThing someOtherThing) {
 return someOtherThing.Gimme(this.someArbitraryField);
}

Better yet, SomeOtherThing would implement an interface and you depend on that interface instead.

Now, you are going to be bombarded with information about "injector objects" like IoC containers, object registries, etc. These days when someone says "DI" or "IoC" they mean "use a container", such as Spring's IoC container, which will "automatically" locate dependencies and inject them. The pattern is *not* that, when in the strictest sense. The pattern is just that which I have described above. You inject dependencies, and do not create them within. :)
 
Last edited:
Back
Top Bottom