0. Background

Image that, we are designing a system/platform/application, which could have multiple kinds of output, like image/video/audio/text/…. Oooh, in fact, every thing has output, whatever you are working on. Facebook shows image/text as comment, amazon generates orders, google list everything you need, links/images/articles….So let us start with the simplest scenario, ImageBox and TextBox, most common controller objects in every MVC coding framework, naming them class ‘TImageBox’ & ‘TTextBox’, to show image and text. To make it object oriented, at least we need one interface for them, let me name it IOutput.

// IOutput.h
#ifndef _I_OUTPUT_H_
#define _I_OUTPUT_H_
#include <string>
#include <stdio.h>

#define OUTPUT_TRACE(fmt, args...)      \
printf("[Output][%s][%d]\t " fmt "\n", Name.c_str(), Index, ## args);
class IOutput
{
public:
    virtual void Initialize() = 0;
    virtual bool Process() = 0;
    virtual void Release() = 0;
protected:
    std::string Name;
    int Index;
};

#endif

1. Interface&Object Definition

As we could see, ‘Index’ is mandatory cuz there could be lots of instance of one output object, like ‘textbox1’, ‘textbox2’…, so an identifier could help alot. Here I defined a ‘Name’ just to make the trace more readable. I’d like to make this interface to support all the output, even for the output objects that has to be initialized before could be used, just as something that deal with hardware, ex, video output, perhaps decoder/frame buffer has to been initialized at first. As for initialize, ‘Release’ becomes mandatory for the close/abort scenario in case other output to use the same resource. OUTPUT_TRACE definition is just to printout the trace. We do not actually have the device to run this trial, so some print to validate our thoughts is helpful.

No we could define and implement our objects, ‘ImageBox’ & ‘TextBox’ as follow

// TImageBox.h
#ifndef _T_IMAGE_BOX_H_
#define _T_IMAGE_BOX_H_
#include "IOutput.h"
class TImageBox: public IOutput
{
public:
    TImageBox(int index)
    {
        Index = index;
        Name = std::string("ImageBox");
        IsInitialized = false;
    };

    // IOutput
    virtual void Initialize()
    {
        // Resource intialize scenario
        // ...
        // Sometimes the initialization would fail due to not released last time
        if (IsInitialized)
        {
            IsInitialized = false;
            OUTPUT_TRACE("Initialization failed, this resources have not been   released last time");
            return;
        }

        IsInitialized = true;
        OUTPUT_TRACE("Initialize completed");
    }
    virtual bool Process()
    {
        OUTPUT_TRACE("Process start...");
        if (!IsInitialized)
        {
            OUTPUT_TRACE("Has not been initialized, process failed");
            return false;
        }
        // Process scenario
        // ...

        OUTPUT_TRACE("Process completed.");
        return true;
    };
    virtual void Release()
    {
        // Resource clean up scenario
        // ....

        IsInitialized = false;
        OUTPUT_TRACE("Release completed");
    };

private:
    // Just a identifier
    bool IsInitialized;
};
#endif
// TTextBox.h
#ifndef _T_TEXT_BOX_H_
#define _T_TEXT_BOX_H_
#include "IOutput.h"
class TTextBox: public IOutput
{
public:
    TTextBox(int index)
    {
        Index = index;
        Name = std::string("TextBox");
        IsInitialized = false;
    };
    // IOutput
    virtual void Initialize()
    {
        // Resource intialize scenario
        // ...
        // Sometimes the initialization would fail due to not released last time
        if (IsInitialized)
        {
            IsInitialized = false;
            OUTPUT_TRACE("Initialization failed, this resources have not been released last time");
            return;
        }

        IsInitialized = true;
        OUTPUT_TRACE("Initialize completed");
    }
    virtual bool Process()
    {
        OUTPUT_TRACE("Process start...");
        if (!IsInitialized)
        {
            OUTPUT_TRACE("Has not been initialized, process failed");
            return false;
        }
        // Process scenario
        // ...

        OUTPUT_TRACE("Process completed.");
        return true;
    };
    virtual void Release()
    {
        // Resource clean up scenario
        // ....

        IsInitialized = false;
        OUTPUT_TRACE("Release completed");
    };

private:
    // Just a indentifier
    bool IsInitialized;
};
#endif
2. Proxy

Here comes the proxy.

What is a proxy pattern? Just go to wiki and he would tell you. For me, proxy allows me to manage all the possible instance in proxy instead of the real scenario. In where I use, the implementation of proxy is enough. When the output switches, just connect the proxy to a new one would be fine. So we could have the proxy defined as below.

// TOutputProxy.h
#include "IOutput.h"
#ifndef _T_OUTPUT_PROXY_H_
#define _T_OUTPUT_PROXY_H_
class TOutputProxy: public IOutput
{
public:
    TOutputProxy();
    void SetOutput(IOutput* output);

    //IOutput
    virtual void Initialize();
    virtual bool Process();
    virtual void Release();
private:
    IOutput* Output;
};
#endif

At first, of course it should derived from IOutput. Also, a real output instance of output should exist as a member to deal with the real work. Then we have the implementation.

// TOutputProxy.cpp
#include "TOutputProxy.h"

TOutputProxy::TOutputProxy():
Output(NULL)
{
    Name = std::string("Proxy");
    Index = 0;
}

void TOutputProxy::SetOutput(IOutput* output)
{
    Output = output;
}

// IOutput

void TOutputProxy::Initialize()
{
if (!Output)
    {
        OUTPUT_TRACE("No output to be initialized in output proxy");
        return;
    }
    Output->Initialize();
    return;
}

bool TOutputProxy::Process()
{
    if (!Output)
    {
        OUTPUT_TRACE("No output in output proxy");
        return false;
    }
    return Output->Process();
}

void TOutputProxy::Release()
{
    if (!Output)
    {
        OUTPUT_TRACE("No output to be released in output proxy");
        return;
    }
    Output->Release();
}

The implementation is just as light-weight as it should be. No real work needs to be done in proxy, the only part is make the real output instance work.

3. Usage

How to use proxy? Or why to use proxy? Image we have a function exist globally in main, like

void Func(IOutput* output)
{
    output->Initialize();
    output->Process();
    output->Release();

// ...
}
void Func_2(IOutput* output)
{
  // Other output scenario 
  // ...
}
void Func_3(IOutput* output)
{
  // Other output scenario 
  // ...
}
// ... Other output function

When the scenario goes more and more complicated, we need to care about what real output to use when the process is executing. Why don’t we make all the function all use a proxy? And just think about the when to connect the real output?

void ConnectOutput(IOutput* output)
{
    Proxy.SetOutput(output);
}

Then all the stuff would be easier like,

// main.cpp
#include "TTextBox.h"
#include "TImageBox.h"
#include "TOutputProxy.h"

TOutputProxy Proxy;

IOutput* Output0;
IOutput* Output1;
IOutput* Output2;
IOutput* Output3;

void OutputInitialize()
{
    Output0 = new TTextBox(0);
    Output1 = new TTextBox(1);
    Output2 = new TImageBox(0);
    Output3 = new TImageBox(1);
}

void ConnectOutput(IOutput* output)
{
    Proxy.SetOutput(output);
}

void Func()
{
    Proxy->Initialize();
    Proxy->Process();
    Proxy->Release();
}

int main()
{
    OutputInitialize();
    ConnectOutput(Output0);
    Func();
    return 0;
}

Let’s just check the print to verify, just make and execute.

### Requires

### Targets
SRC += *.cpp
TARGET += ProxyDemo

##Build
edit: $(SRC)
    ;g++ $(SRC) -o $(TARGET)

Here is the print,

[Output][TextBox][0] Initialize completed
[Output][TextBox][0] Process start...
[Output][TextBox][0] Process completed.
[Output][TextBox][0] Release completed

 

Advertisements