A Simple Implementation of MVC Framework

Most of us uses lots of Java MVC Framework in the software development. The famous will be Spring. Maybe you are scared away to think to implement yours when you saw the huge code in the Spring framework. Actually, it is not so hard that you just implement a simple MVC application without the use of Spring framework.

In the article, we will demo a simple MVC of inventory management mobile APP in text mode (not gui), which include product in, product out and inventory tracking.  We will use Chain of Responsibility and Observer design pattern to implement, with test-driven and refactoring involved step by step.

Part I: Let’s Get Started

1. Part 1: The Stories that we will implemented.

For which of you, desiring to build a tower, does not first sit down and count the cost, whether he has enough to complete it? Otherwise, when he has laid a foundation and is not able to finish, all who see it begin to mock him, saying, ‘This man began to build and was not able to finish.’

Without a detail plan, it is hard for anyone to finish any simple project.  We start from our story flow. (Ref: https://www.strategicerp.com/inventory-management.php)

The real-life inventory is as below:

  1. We maintain product items and their inventory
  2. We got sales order (SO) from customers.
  3. We create purchase order (PO) to buy product if the product in the inventory is not enough
  4. When Product is shipped to our warehouse, we increase our inventory.
  5. When all products in a sales order ready to ship to customer, we ship out to our customer.
  6. When ship out to our customer, the inventory decrease.

To make it simple in our Java implementation, we only have one product to purchase and sell, and we only have one customer and one vendor. Of course, we will have many purchase orders and sale orders. When we input the PO, we assume products are instantly shipped to our warehouse and increase our inventory. If there is not enough inventory when a new SO is input, we will get alert in the Main screen.

Here is the screen simulator layout in text mode:

(Diagram 0: the screen layouts)

2. Part 2: The MVC Brainstorm and the System Flow

Standing on the shoulders of giants

MVC stands for Model, View and Controller.  That is a proven successful framework in the modern software engineering world. One example is that Microsoft phases out its Web Form framework, and hug to the MVC in its Visual Studio package.

The model in our project will includes PO, SO, Product. We will keep our data in the memory only, no database covered here.

The system Controller will include the AppController (the flow control of the whole system) and the screen controllers (each screen will have one controller).

The View will include PO input, SO input, Product Inventory.

The system will simulate the whole flow with console commands below, instead of GUI.

  1. PO:  input a new PO
  2. SO: input a new SO
  3. PO list: list the last 5 PO
  4. SO list: list the last 5 SO
  5. Product: list the inventory
  6. Touch(x,y): touch (x,y) to trigger event of the screen
  7. Menu command
  1.  “M”: back to main screen
  2.  “P”:  go to PO input screen
  3.  “S”:  go to SO input screen

Screen Detail: The screen layout will be coordinated as 9X3 cell

  1. Main Screen:  X means column 1 or 2, or 3
  1. Touch (1,X) will new PO
  2. Touch (2,X) will new SO
  3. Touch (3,X) will List PO
  4. Touch (4,X) will List SO

  1. New PO Screen:  PO# will be auto generate
  1. Input product qty in cell (1,X)
  2. Input PO date time in cell (2,X) with format yyyymmdd, such as 20181008
  3. Two buttons, one is “Cancel” in cell (3,1), another is “Save” in cell (3,3)
  4. A keypad screen will show from cell(4,X) to cell(9,X)
  1. New SO Screen
  1. Input product qty in cell (1,X)
  2. Input SO date time in cell (2,X)
  3. A keypad screen will show to input
  1. Product Inventory (to be simple, it will be implemented into Main Screen)
  1. Show product name(Laptop here) and inventory:  for example “Laptop 2”
  1. PO List screen
  1. List last 5 PO in from Line 1 to Line 5.
  2. When you Touch (n,X), it will goto the Last nth PO view screen

  1. SO List screen
  2. PO 1: view the #1 PO
  3. SO 1: view the #1 SO
  4. You have menu system in all screen for below function:
  1. Click “M”: back to main screen
  2. Click “P”:  go to PO input screen
  3. Click “S”:  go to SO input screen

Part 3: Kick off the Eclipse Java Project step by step

“The longest journey starts with a single step.”

Do not be panic for the above project description.  We can start it with a small step.

  1. First create the Eclipse JAVA project, put your project folder in your project folder, such as “E:projectsInventory”.

When you create the project, remember to add “Junit 4” in the project Library.  We will use the Test Driven method for our whole project.  Here is the folder structure after you create a project.

  1. As we need to use the AppController to test the menu item to show different screens, we need to create the Screen class first.

  1. Let’s create our AppController first.  We will set all class into “Inventory” package.

After create the AppController.java, we will create AppControllerTest.class for unit test.

And we will initialize those screens (Main Screen, PO input Screen, SO input Screen) in the AppController.java.

           AppController(){

                main= new Screen(“Main”);

                poInput = new Screen(“PO Input”);

                soInput = new Screen(“SO input”);

        }

               When the system initiates, the default screen will be Main screen. To allow our switch to different screens in the system, we will add Frame.setCurrent as the agent of AppController to switch current screen.

public class Frame {

        private Screen current;

        

        /**

         * Set current screen for the system.

         * @param screen

         */

        public void setCurrent(Screen screen) {

                this.current=screen;

        }

}

So, our system will have one frame, and several screens.

Here is our first test case and result:

This finish our project kick-off.

Part 4-pre: create complex screens from simple components

A picture is worth a thousand words

Ok. Let’s start on the screen view.

4a. Two Major UI components in screens

In our screens (ref Diagram 0: the screen layouts) , we will have keypad to input and need to touch button in the screen. So, basically, we will have two different UI components: first is keypad UI, second is touch UI. Each UI component has two major features:

  1. Receive the touch event: for example, keypad input
  2. An UI to show users input.

But, not all screens will have the two UI components. For example, in PO View screen, we do not provide Keypad UI component, as we do not require any input for the screen.

  1. The Keypad UI layout component:

it is (3X5) layout, which will be shown in the bottom of the screen.

  1. An example of PO input touch UI component:

4b) Screen Accessory: Navigate Pre and Next button, and Menu Items.

In each screen, we will have Pre and Next button at the top of screen, and menu items (M,P,S) at the bottom of the screen.

When combine the two UI components and Accessory, below is a PO Input screen:

(Diagram 1: PO Input Screen)

2c. Screens Layout Detail explanation in text mode

  1. All screens will be 8×5 cells layout.
  2. Not all screens has Pre and Next. If a screen does not have the button, the button will not show.
  3. The screen will be able to show the screen layout in text format when the application call show() to show screen in console, or call screenText() to return the screen text info.

If there is no info in the cell, it will show a space; else show the info as screen layout mockup.

  1. An text-mode screen of the Diagram 1: PO Input Screen: (total is 8 lines)

Pre     Next

  PO Input

 QTY:12

 Date:20181008

 Cancel Save

12345

67890

  X

(Diagram 2: screen info of the PO Input)

4. Part 4: Implement the PO Input function.

We don’t grow when things are easy; we grow when we face challenges.

4.1 Let’s create the MVC skeleton parts for the PO Input function.

According to MVC framework, we need to have Model and Controller and the View, we will implement the view first. Here, it will be the UI components.

4a) first, let’s create the Model first.

We will have PO.java to keep the information.  

public class PO {

        int id;

        String item=“Laptop”;

        int qty;

        String date;

}

In the AppController.java constructor, we will have POList to contain all those PO, POList= new ArrayList<PO>();

4b) Create blank PO Input Controller:  POInputController.java

4c) Create Two blank UI components: UIKeyPad.java and UIMain.java, and in the AppController, initializes a Screen POInput object, which will be used to be the composition containers of the two UI components.

4.2 Connect the MVC skeleton parts into a car

Small streams make large rivers.

So far in 4.1, those parts (UI/Screen, Controller, Model) of the MVC are separated, we need to glue them together to make a real functional skeleton.

  1. View: add the 2 UI components in the POInput screen.

To do that, we need to add addUIComponent(IUI ui) function in the Screen.java to enable AppController to setup our screen. To make it easy for addUIComponent parameter , we will add IUI.java interface, our two UI components will implement the IUI interface. Here is the initialized code in AppController constructor. We need to add MainUI first then add KeyPad as this is the UI sequence in the poInput screen layout.

                keypad=new UIKeyPad();

                mainUI_poInput= new UIMain();

                poInput.addUI(mainUI_poInput);

        poInput.addUI(keypad);

  1. Implement the Touch event passed down from AppController to Two UI components.
  1. Create a ITouchEventActor. Java interface.

The two UI will implements the touch(int x, int y) function.  Below is the so-far UIKeypad.java code

public class UIKeyPad implements IUI,ITouchEvent {

        private String name=“KeyPad”;

        public String getName() {return name;}

        

        public void touch(int x,int y) {

                

        }

}

  1. Setup an event link in screen such that the screen will pass touch event to two UI components.  

Here we will use Chain of Responsibility Design Pattern.

When app.touch() will trigger frame.touch(), then current screen.touch, then keypad.touch() or MainUI.touch().

So far, we already know below functions are used in the AppController.java: app.screen(),app.touch(), app.execute(). It will have more and more functions, such as prev(), next(), etc. To be easily for test, we group those methods into an interface IApp.java and let AppController.java to implement it.

We will add touch() method in the AppController, Frame, Screen.

In AppController, public void touch(int x, int y) {frame.touch(x,y);}

In Frame, public void touch(int x,int y) {current.touch(x,y);        }

AppController and Frame only pass down the (x,y) to Screen, Screen.java will be logic to pass the (x,y) to each UI components.  

According to poInput screen layout,

  1.  Keypad will handle the touch event when the touch row # is 5,6, 7.
  2. MainUI will handle the touch event when the touch row # is 0,1,2,3,4.

However, as Screen.java will create the UI components at run time, Screen.java has no idea on which UI components attached to the screen. So, we need to implement the logic into UI components. Here, UI components are now treated as touch event actor, we should create interface ITouchEventActor.java, and have two UI components implement it.  We will use the interface to implement a simple touch event actor link.

public interface ITouchEventActor {

        public void touch(int x,int y);

        public void setNext(ITouchEventActor next);

}

Here is the event implementation of UIKeyPad.java

        public void touch(int x,int y) {

                if(x>=5) {

                        String key=getKey(x,y);  //todo

System.out.print(“keypad touch at “+x+“,”+y);

                }else {

                        if(nextEventActor!=null) nextEventActor.touch(x,y);

                }

        }

        public void setNext(ITouchEventActor next) {

                this.nextEventActor=next;

        }

Here is the event implementation of UIMain.java

        public void touch(int x,int y) {

                if(x<5) {

                          //todo: notify the screen controller.

System.out.print(“Main UI touch at “+x+“,”+y);

                }else {

                        if(nextEventActor!=null) nextEventActor.touch(x,y);

                }

        }

        public void setNext(ITouchEventActor next) {

                this.nextEventActor=next;

        }

Ok, we need to setup the event link in the Screen when we add UI component to the screen. That is easy. The event link will always point to the first UI component and the first UI component will set its next event actor if have 2nd UI component. In our App, we only have max of two UI components. Here is the event link implementation in Screen.java. You can expand the code for more than 2 UI components.

                public void addUI(IUI ui) {

                UIs.add(ui);

                if(UIs.size()==1) eventLink=(ITouchEventActor)ui;

                else if(UIs.size()==2) eventLink.setNext((ITouchEventActor)ui);

        }

        

                public void touch(int x, int y) {

                        eventLink.touch(x, y);

}

  1. So far, we have do many code changes. Let’s try to test whether the event will pass correctly from AppController to keypad and Main UI.

We will try catch the System.out.println from keypad and mainUI for junit Assert command.

*) first try fail:

It mentions NullPointException. Oh, we forget to change current screen to PO input screen, as the default screen for our App is Main screen.

Yes. After several debug, here is the final pass test case.

Congratulate! The touch event can pass down to our two UI components.

  1. Using screen Controller to handle the touch event.

We will use screen controller to play role to handle the event from Main UI component and the keypad component. Each Screen will have its controller to handle how to display/store information in the screen.  We will have POInputController.java to handle PO input screen event and UI screen info update.

We will use the Observer pattern to implement the controller to handle the event subject. As keypad will provide key (for example, 1,2,3..0) to controller, and main UI need to provide the (x,y) position to controller. So, the two UI components have different parameter when notify controller. We have to create keypad observer and mainUIObserver. Here are the two interfaces and our POInputController.java.

public interface IMainUIObserver {

        public void touchEventUpdate(int x, int y);

}

public interface IKeyPadObserver {

   public void keyEventUpdate(String key);

}

public class POInputController implements IMainUIObserver,IKeyPadObserver  {

        public void keyEventUpdate(String key) {

                

        }

        public void touchEventUpdate(int x, int y) {

                

        }

}

Also, the keypad and Main UI need to implement the event subject interface IKeyEventSubject and IMainUIEventSubject to notify all observers. The keypad and the MainUI needs to have feature to attach/collect its observers.

public interface IEventSubject {

        void notifyObservers();

        void attach(Object o);

}

public class UIKeyPad implements IUI,ITouchEventActor,IEventSubject

public class UIMain implements IUI,ITouchEventActor,IEventSubject

From the implementation of the 3 interfaces of UI components, we can see our UI components have more and more functions now. First, it shows UI info, and then receive Touch event from screen, and finally notify touch event to controller.

Here is the IEventSubject implementation in UIKeyPad.java and MainUI.java accordingly. The touch event will notify all observers.

———- in UIKeyPad—–

        public void notifyObservers() {

                for(IKeyPadObserver o:observers) o.keyEventUpdate(key);                

        }

        public void attach(Object o) {

                observers.add((IKeyPadObserver)o);

        }

        public void touch(int x,int y) {

                if(x>=5) {

                        key=getKey(x,y);  //todo: now alway 1;

                        System.out.print(“keypad touch at “+x+“,”+y);

                        notifyObservers();

                }else {

                        if(nextEventActor!=null) nextEventActor.touch(x,y);

                }

        }

———–in MainUI —

        public void notifyObservers() {

                for(IMainUIObserver o:observers) o.touchEventUpdate(x, y);        

        }

        public void attach(Object o) {

                observers.add((IMainUIObserver)o);

        }

        public void touch(int x,int y) {

                if(x<5) {

                          //todo: notify the screen controller.

                        System.out.print(“Main UI touch at “+x+“,”+y);

                        notifyObservers();

                }else {

                        if(nextEventActor!=null) nextEventActor.touch(x,y);

                }

        }

To make our POInoutController easier, we combined two event observers interface into a new interface IEventObserver;

public interface IEventObserver extends IMainUIObserver,IKeyPadObserver

In AppController.java we will attach our POinputController as the Keypad and MainUI event observer.

pOInputController=new POInputController();

                ((IEventSubject)mainUI_poInput).attach(pOInputController);

                ((IEventSubject)keypad).attach(pOInputController);

And, here is the initial POInputController implementation:

public class POInputController implements IEventObserver  {

        public void keyEventUpdate(String key) { //key is just 1 so far.

                System.out.print(“PO Input Screen Accept Keypad touch for key “+key);

        }

        public void touchEventUpdate(int x, int y) {

                System.out.print(“PO Input Screen Accept Main UI touch at “+x+“,”+y);

        }

}

The above is to create a path to pass events from Appcontroller, to Frame, to current screen and to UI component and then finally go to our screen controller POInputController.

Let’s try add test case to see whether it can pass event to controller or not. After several rounds of debug, it works!

 4.3 Implement the real story of our POInput screen

Now, it is time to implement the real story of our POInput screen.

Ref to: (Diagram 1: PO Input Screen), the story is as below:

*a) user may set input focus on the Qty or on the Date.

*b) user will use keypad to input the qty or the date(format: yyyymmdd)

*c) only valid qty (not blank) and date can be save to a PO

*d) when users click cancel will back to Main screen.

*e) when users click save, it will validate PO data and save if valid and clear all fields and stay in the same POInput screen, else still keep in the screen but show msg “invalid PO info” in cell(4,1)

As now we will provide output to our screen, we need to have app.screenInfo() to get the current screen information. You can refer (diagram 2: screen info of the PO Input)

For the PO Input screen information example.

Similar as event pass down, we will pass down the get screen information from AppController, to Frame, to Screen and to UI Components, as below codes.

public String screenInfo() {return frame.screenInfo();}  //in AppController

public String screenInfo() {return current.screenInfo();} // in Frame

In Screen.java, it will combine all UI components information to return to frame request. Here is the code:

        public String screenInfo() {

                String content=“”;

                for(IUI ui:UIs) {

                        content+=ui.screenInfo();

                }

                return content;

        }

So, for each UI components, we will define its screen content accordingly.

  1. Keypad ui:

It is simple, static and consistent for every screen. The screen info in the UIKeypad.java will be as below:

        public String screenInfo() {

                return “12345n67890nBack”;

        }

  1. Main UI:

It is a little complex and varies for screens. The content may be dynamically changed according to the user input. For example, in our PO Input screen, if users input key 1, the Main UI will show 1. If users input another 2, the Main Ui content will change to “12”.

As the content maybe change dynamically, we need to set the Main UI content by the screen controller.

Well, it smells like we talked before. Yes, that is the Observer design pattern again. Main UI components will be the Observer of our screen controller. To do that, similarly IEventObserver and IEventSubject, we can add IControllerSubject and IControllerObserver interface to implement the design pattern. However, seems it is too complex for just set the content of UI component.

Another way to do that is just add the Main UI object reference to the Screen Controller when controller is initialized, so that Controller can update the content when necessary. We will use this approach. Here is the code in UIMain.java

        public String screenInfo() {

                return content;

        }

        public void setContent(String content) {this.content=content;}

and the change of controller constructor to add UI reference:

public POInputController(IUI ui) {        mainUI=ui;        }

and code update in AppController:

pOInputController=new POInputController(mainUI_poInput);

So far, we already setup the path on how to update screen content when appController call touch event. Now it is time to setup the real screen content.

  1. PO Input Screen content implementation

Re-copy the PO Input screen story here.

Ref to: (Diagram 1: PO Input Screen), the story is as below:

*a) user may set input focus on the Qty or on the Date.

*b) user will use keypad to input the qty or the date(format: yyyymmdd)

*c) only valid qty (not blank) and date can be save to a PO

*d) when users click cancel will back to Main screen.

*e) when users click save, it will validate PO data and save if valid and clear all fields and stay in the same POInput screen and show “Success!” in cell(4,1), else still keep in the screen but show msg “Invalid PO” in cell(4,1)

3a) Implement the PO Input screen content without save and cancel action

That is not difficult, we just need to handle the input focus change and the qty and date data change from the Main UI touch event and keypad event. Here is the code.

public class POInputController implements IEventObserver  {

        private IUI mainUI;

        private int id;  //todo

        private String qty;

        private String date;

        private boolean qtyInput;  //current is inputting qty if true, else date input.

        

        public POInputController(IUI ui) {        mainUI=ui;init();}

        private void init() {qtyInput=true;qty=“”;date=“”;}

        public void keyEventUpdate(String key) { //key is just 1 so far.

                System.out.print(“PO Input Screen Accept Keypad touch for key “+key);

                if ( key.equals(“Back”) ) {

                        if (qtyInput && qty.length()>0) qty=qty.substring(0, qty.length()-1);

                        else if (!qtyInput && date.length()>0) date=date.substring(0, date.length()-1);

                }

        else {

                if(qtyInput) qty=qty+key;

                else date=date+key;

        }

                setContent();

        }

        public void touchEventUpdate(int x, int y) {

                System.out.print(“PO Input Screen Accept Main UI touch at “+x+“,”+y);

            if(x==2 && y>=2) qtyInput=true;

            else if(x==3 && y>=2) qtyInput=false;

        }

        public void setContent() {

                String content=“Pre     Nextn  PO Inputn”;

                content+=“QTY:”+qty+“n”;

                content+=“Date:”+date+“n”;

                content+=”  Cancel Save”;

                mainUI.setContent(content);

        }

}

Let’s try the test case to see whether it works or not. The test case will try change input focus and type some key to see whether the screen can show the correct input. It works as test case passes.

  1. Implement the PO Input “Save” button action

 According to story, we need to validate the Date/Qty before save the PO to our PO list and show “Success!”, If not valid, we will show “Invalid PO” in cell(4,1).

Now, we need to create our Model Object PO and its POManager.java.

POManager will be functioned as addPO, validatePO and getPO. Also, PO Input controller(act as IPOSubject) to notify POManager(act as IPOObserver) to add PO. Here is the POManager code:

public class POManager implements IPOManager,IPOObserver {

        ArrayList<PO> POList;

        public POManager() {

                POList=new ArrayList<PO>();

        }

        

        private boolean validatePO(PO po){return (po.qty>0 && Helper.isDateValid(po.date));        }

        public PO getPO(int id){return (POList.get(id));}

        public boolean PONotify(String action,PO po) {

                if(action.equals(“add”) && po!=null)

                        if (validatePO(po)) {POList.add(po);return true;}

                return false;

        }

}

Also, we need to initialize POManager in the AppController.java as below:

                pm=new POManager();

                ((IPOSubject)pOInputController).registerPOObserver(pm);

Below is the code add in POInputController.java

        public void notifyPOObservers() {

                if (qty.length()==0 || date.length()==0) {alertMessage=“Invalid PO!”;setContent();return;}

                po =new PO(Integer.valueOf(qty),date);

                if(poObserver.PONotify(“add”, po)) {alertMessage=“Success!”;init();}

                else {alertMessage=“Invalid PO!”;setContent();}

        }

        public void registerPOObserver(IPOObserver pm) {poObserver=pm;        }

        public void touchEventUpdate(int x, int y) {

                System.out.print(“PO Input Screen Accept Main UI touch at “+x+“,”+y);

            if(x==2 && y>=2) qtyInput=true;

            else if(x==3 && y>=2) qtyInput=false;

            else if(x==4 && y==5) notifyPOObservers();

        }

Ok, time to test case again. Yes. It passes finally.

  1. Implement the PO Input “Cancel” button action

When users press “Cancel”, it will go to “Main” Screen. We need to implement the “Main” screen first. That is easy as Main screen only has buttons without keypad. The whole screen will have Main UI only. Here is the Main Scree layout

So, the Po Input Screen Controller should have the Main screen reference if the Controller wants to navigate from PO input screen to Main screen. Further, a screen may be able to navigate multiple screens. We need to expand the Controller to let them get the navigate screen list when controller is constructed. Of course, controller needs to have frame instance to set current screen to navigate.

public POInputController(Frame f,Screen[] s,IUI ui) {frame=f;screens=s;        mainUI=ui;init();}

Previously, in PO input scrren, the UIMain.java only handle the half at top screen and keypad handle the half at bottom. Now, in the Main screen, UIMain.java needs to expand to handle either half or full screen. We use the Boolean fullScreen variable to label it when UIMain object constructs. The update UIMain.java code as below:

        public UIMain(boolean fullScreen) {

                this.fullScreen=fullScreen;

                observers = new ArrayList<IMainUIObserver>();

        }

        public void touch(int x,int y) {

                if(fullScreen || x<5) …

also, add the code below in appController to init Main screen as below:

                //Main Screen

                mainUI_Main= new UIMain(true);

                main.addUI(mainUI_Main);

        mainController=new MainController(new Screen[]{poInput,poList},mainUI_Main);

The Main screen Controller code is simple to just have navigate feature only.

public class MainController implements IMainUIObserver  {

        private IUI mainUI;

        private Screen[] screens;

        private Frame frame;

        public MainController(Frame f,Screen[] s,IUI ui) {frame=f;screens=s;mainUI=ui;init();}

        

        private void init() {setContent();}

        public void touchEventUpdate(int x, int y) {

            if(x==2 && y>=2) frame.setCurrent(screens[0]);

            if(x==3 && y==2) frame.setCurrent(screens[1]);

        }

        public void setContent() {mainUI.setContent(“n  Mainn PO Inputn PO List”);        }

}

Also, we need to update PO input screen to navigate to Main screen when click app.pre() and Cancel, and Stay in PO input screen when click app.next(). Here is the code update in POInputController.java.

        public void touchEventUpdate(int x, int y) {

                System.out.print(“PO Input Screen Accept Main UI touch at “+x+“,”+y);

            if(x==2 && y>=2) qtyInput=true;

            else if(x==3 && y>=2) qtyInput=false;

            else if((x==4 && y==5)|| (x==0 && y==5)) notifyPOObservers();

            else if((x==4 && y==3) || (x==0 && y==1)) frame.setCurrent(screens[0]);

        }

Here we use touch(0,1) as app.pre and touch(0,5) as app.next. AppController.java  adds below codes:

  public void pre() {touch(0,1);}

  public void next() {touch(0,5);}

OK. It is time to test case to check PO input navigate to Main screen and Main Screen navigate to PO input screen. It passes!

Now, we finish implement all the features of PO Input screen.

  1. Part 5: Implement the PO List Screen function.

The PO list will show the last 5 PO. Here is the screen layout. If click Pre, it will back to Main. The screen is similar as Main screen.

The POListController should be the observer of POManager. If any PO change in POManager, the POManager will notify POListController to update its content. We create IPOChangeSubject and IPOChangeObserver to implement the Observer pattern.

The IPOChangeObserver is different than previous IPOObserver, which POManager is the Observer of the POInputController.

Here is the POManager code added to notify IPOChangeObserver.

        public String POList() {

                String content=“”;

                int i=0;

                for(int j=count()-1;i<LIST_RETURN && j>=0;j–) {

                        PO po=POList.get(j); i++;

                        content+=“PO “+j+“: “+ “QTY:”+po.qty+” Date:”+ po.date+“n”;

                }

                return(content);

        }

        public void attach(IPOChangeObserver o) {observers.add(o);}

        public void notifyObservers() {        for(IPOChangeObserver o:observers) o.poUpdate(this);}

Here is the POListController:

public class POListController implements IMainUIObserver,IPOChangeObserver  {

        private IUI mainUI;

        private Screen[] screens;

        private Frame frame;

        private POManager pm;

        

        public POListController(Frame f,Screen[] s,IUI ui) {frame=f;screens=s;mainUI=ui;init();}

        

        private void init() {setContent();}

        public void touchEventUpdate(int x, int y) {

            if(x==0 && y>=1) frame.setCurrent(screens[0]);

        }

        public void setContent() {mainUI.setContent(“Pren  PO Listn”+(pm==null?“”:pm.POList()));        }

        public void poUpdate(POManager pm) {this.pm=pm;setContent();}

}

Here is the pass test case: we create two PO first, then show the PO in the PO List screen.

Part II, Implement the SO Input and SO List

  1. Time to refactor

Seems the SO input and SO list are similar as PO input and PO List. We will try to extend the original POInputController and POListController src code.

*1) refactor PO.java to Order.java and add type property to indicate PO or SO

*2) POInputController -> OrderInputController

*3) POManager-> OrderManager

*4) All IPOxx Interface will refactor to IOrderxxx.

Below is the file list after refactoring. Actually, it is very easy to do that using the Eclipse Rename feature to refactor.

And try re-run test case after refactoring, it still works.


  1. New features with SO in Order Manager
  1. Inventory field

We need to have a new inventory field in order manager to be updated when a so and po is added. When SO is added, inventory will be subtracted with SO qty; While add PO qty when PO is added.

  1. Classified order type in Order Manager when get order list and notify observer.

Code is updated as below.

        public String orderList(String type) {

                String content=“”;

                int i=0;

                for(int j=count()-1;i<LIST_RETURN && j>=0;j–) {

                        Order order=orderList.get(j);

                        if (order.type.equals(type)) {

                                i++;

                                content+=type+” “+j+“: “+ “QTY:”+order.qty+” Date:”+ order.date+“n”;

                        }

                }

                return(content);

        }

        public void notifyObservers(String type) {        for(IOrderChangeObserver o:observers) o.orderUpdate(this,type);}

  1. Ok, let’s initialize the OrderInputController for SO Input, and OrderListController for SO List in the AppController. The two controllers are now used by PO Input and PO List accordingly. Here is the code added in AppController.java

                soInput = new Screen(“SO Input”);

                soList=new Screen(“SO List”);

                //SO Input Screen

                keypad2=new UIKeyPad();

                mainUI_soInput= new UIMain(false);

                soInput.addUI(mainUI_soInput);

                soInput.addUI(keypad2);

                

                sOInputController=new OrderInputController(frame,new Screen[]{main} ,mainUI_soInput,“SO”);

                ((IEventSubject)mainUI_soInput).attach(sOInputController);

                ((IEventSubject)keypad2).attach(sOInputController);

                ((IOrderSubject)sOInputController).registerPOObserver(pm);                

                //SO List Screen

                mainUI_SOList= new UIMain(true);

                soList.addUI(mainUI_SOList);

                soListController=new OrderListController(frame,new Screen[]{main},mainUI_POList,“SO”);

                ((IEventSubject)mainUI_SOList).attach(soListController);

                ((IOrderChangeSubject)pm).attach((IOrderChangeObserver)soListController);

Wow, so easy to add SO Input and SO list after refactor, as we reuse the code from PO Input and PO list.

3. Let’s try test case for SO Input and SO List.

When you create a new test case in Eclipse, try New->Class->Others-> select Junit->Jnit Test Case. It will generate a Junit test skeleton for you.

3a) First, we need to test the navigate flow from Main to SO Input and SO List

3b) SO Input and SO List test case

That will be easy. You just need to use the previous PO Input and PO List test case with replacement of “PO” with “SO” and change touch location of SO List in Main screen. It is not difficult to pass the new SO input and SO List test case.

3c) Almost done!

 We need to modify the Main screen to show the inventory alert when sum of SO qty is larger than sum of PO qty. Below is the sample Main screen layout for “Not Enough Inventory”

It is clear that MainController should be the observer of OrderManager. We need to update the Main screen info when an PO is added or SO is added.

First, we need to update our IOrderChangeObserver to notify the current inventory.

public interface IOrderChangeObserver {

        /**

         * notify observer for a specify order type is updated

         * @param om   order manager

         * @param type an order is updated

         * @param inventory updated inventory

         */

        void orderUpdate(OrderManager om,String type,int inventory);

}

Next update MainController to implement the IOrderChangeObserver interface. Here is the code.

public class MainController implements IMainUIObserver,IOrderChangeObserver  {

        private IUI mainUI;

        private Screen[] screens;

        private Frame frame;

        public MainController(Frame f,Screen[] s,IUI ui) {frame=f;screens=s;mainUI=ui;init();}

        private int inventory=0;

        private String inventoryAlert=“”;

        private final static String ALERT=“Not Enough Inventory”;

        

        private void init() {setContent();}

        public void touchEventUpdate(int x, int y) {

            if(x==2 && y>=2) frame.setCurrent(screens[0]);

            if(x==3 && y>=2) frame.setCurrent(screens[1]);

            if(x==4 && y>=2) frame.setCurrent(screens[2]);

            if(x==5 && y>=2) frame.setCurrent(screens[3]);

        }

        public void setContent() {mainUI.setContent(“n  Mainn PO Inputn PO Listn SO Inputn SO Listn”+” Inventory:”+inventory+“n “+inventoryAlert);}

        

        public void orderUpdate(OrderManager pm,String type,int inventory) { if(inventory<0) inventoryAlert=ALERT; else inventoryAlert=“”;setContent();        }

}

3d) Time to the inventory test case.

First, we add a SO with qty 1260, it will get alert in the Main screen. Then we add a PO with qty 1250. The alert will be still kept in Main screen. Then we add another PO with qty 10. The alert will gone.

Let’s do it one by one.

*a) Check Inventory Alert when input a SO with qty 1260.It should show alert.

*b) Check Inventory Alert when input a PO with qty 1250. The alert stays there as SO QTY is larger than PO QTY. It passes as below.

*c) Input one more PO to make inventory larger than 0. So no more alert. Yes. It passes as below.

Part III. Implement the mobile App simulator

The inventory mobile App has passes all the test case. However it can’t run in the console. Let’s add a simulator to run the app in text mode in console.

To run the simulator, we need to have a Main.java class to initialize the AppController, and we need to receive the input from users and show the screen info in the console.

Here is the console command:

Key xxxx: input by keypad with xxxx

Pre: back to Main screen

Next: save for some screens

Date: change the current input focus to Date field

QTY:  change the current input focus to QTY field

Key b: input field delete last number

M: go to Main screen

P: go to PO Input screen

S: go to SO input screen

PL: go to PO List screen

SL: go to SO List screen

Save: press save button

Cancel: press cancel button, back to Main screen

Here is the console simulator, run in the Bin folder with command “java -cp . inventory.Main”

Part Iv. Summary

We finish the whole simulator of simple inventory management App in text mode. It uses the MVC concept to be easy to understand and implement. You may brainstorm your story to implement similar one. It will help to understand the junit test, the design pattern and mvc model.






Leave a Reply