C++實現設計模式: PIMPL IDIOM

I learned this from the book Professional C++. And code this example to deepen the this practical pattern.
The whole design is very compact, and very practical. I think that it is very suitable for applications in the future as an example of engineering practice. Hope you like it.

I have drew the UML class diagrams  for overview the classes:


SpreadsheetCell.h
#ifndef SPREAD_SHEET_CELL_H
#define SPREAD_SHEET_CELL_H

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

class Spreadsheet;

class SpreadsheetCell
{
public:
    typedef enum {Red=1, Green, Blue, Yellow} Colors;
    
    SpreadsheetCell& operator+=(const SpreadsheetCell& rhs);
    SpreadsheetCell& operator-=(const SpreadsheetCell& rhs);
    SpreadsheetCell& operator*=(const SpreadsheetCell& rhs);
    SpreadsheetCell& operator/=(const SpreadsheetCell& rhs);
    const SpreadsheetCell operator-() const;
    
    SpreadsheetCell(double initialValue = 0.0);
    explicit SpreadsheetCell(const string& initialValue);
    SpreadsheetCell(const SpreadsheetCell& src);
    SpreadsheetCell& operator=(const SpreadsheetCell& rhs);
    
    void set(double inValue);
    void set(const string& inString);
    void setColor(Colors color);

    double getValue() const;
    string getString() const;
    
    const SpreadsheetCell operator+(const SpreadsheetCell& cell) const;
    const SpreadsheetCell operator+(double rhs) const;
    SpreadsheetCell& operator++(); //Prefix
    SpreadsheetCell& operator--(); //Prefix
    SpreadsheetCell operator++(int); //Postfix
    SpreadsheetCell operator--(int); //Postfix
    
    friend class Spreadsheet;
    //Supply the friend method is not a good idea, it will break the encapsulation.
    friend bool checkSpreadsheetCell(const SpreadsheetCell& cell);
    friend const SpreadsheetCell operator+(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend const SpreadsheetCell operator-(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend const SpreadsheetCell operator*(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend const SpreadsheetCell operator/(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    
    friend bool operator==(const SpreadsheetCell& lhs,const SpreadsheetCell& rhs);
    friend bool operator<(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend bool operator>(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend bool operator!=(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend bool operator<=(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    friend bool operator>=(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs);
    
    friend ostream& operator<<(std::ostream& ostr, const SpreadsheetCell& cell);
    friend istream& operator>>(std::istream& istr, SpreadsheetCell& cell);
    
protected:
    static string doubleToString(double inValue);
    static double stringToDouble(string inString);
    double mValue;
    string mString;
    
    mutable int mNumAccesses;
    Colors mColor;
};


#endif

Spreadsheet.h
#ifndef SPREAD_SHEET_H
#define SPREAD_SHEET_H

#include "SpreadsheetCell.h"
#include "SpreadsheetImpl.h"

class SpreadsheetApplication;
//class SpreadsheetImpl;
class Spreadsheet
{
public:
    Spreadsheet(const SpreadsheetApplication& theApp, int inWidth, int inHeight);
    ~Spreadsheet();
    Spreadsheet(const SpreadsheetApplication& theApp);
    Spreadsheet(const Spreadsheet& src);
    Spreadsheet& operator=(const Spreadsheet& rhs);
    
    void setCellAt(int x, int y, const SpreadsheetCell& inCell);
    SpreadsheetCell getCellAt(int x, int y);
    int getId() const;

protected:
    SpreadsheetImpl* mImpl;
    
};

#endif


SpreadsheetImpl.h
#ifndef SPREAD_SHEET_IMPL_H
#define SPREAD_SHEET_IMPL_H
#include "SpreadsheetCell.h"

class SpreadsheetApplication;
class SpreadsheetImpl
{
public:
    static const int kMaxHeight = 100;
    static const int kMaxWidth = 100;

    explicit SpreadsheetImpl(const SpreadsheetApplication& theApp, 
        int inWidth = kMaxWidth, int inHeight = kMaxHeight);

    SpreadsheetImpl(const SpreadsheetImpl& src);
    ~SpreadsheetImpl();
    SpreadsheetImpl& operator=(const SpreadsheetImpl& rhs);
    
    void setCellAt(int x, int y, const SpreadsheetCell& inCell);
    SpreadsheetCell getCellAt(int x, int y);
    int getId() const;
     
private:
    bool inRange(int val, int upper);
    void copyFrom(const SpreadsheetImpl& src);
    int mWidth, mHeight;
    int mId;
    SpreadsheetCell** mCells;
    const SpreadsheetApplication& mTheApp;
    static int sCounter;
    
};

#endif


SpreadsheeetCell.cpp
#include "SpreadsheetCell.h"


SpreadsheetCell::SpreadsheetCell(double initialValue):
    mValue(initialValue),mString(doubleToString(initialValue))
{
    mNumAccesses =0;
    mColor = Red;
}

SpreadsheetCell::SpreadsheetCell(const string& initialValue):
    mValue(stringToDouble(initialValue)),mString(initialValue)
{
    mNumAccesses =0;
    mColor = Red;
}

SpreadsheetCell::SpreadsheetCell(const SpreadsheetCell& src)
    :mValue(src.mValue),mString(src.mString)
{
    mNumAccesses = src.mNumAccesses;
    mColor = src.mColor;
}

SpreadsheetCell& SpreadsheetCell::operator=(const SpreadsheetCell& rhs)
{
    if(this == &rhs)
    {
        return *this;
    }
    this->mValue = rhs.mValue;
    this->mString = rhs.mString;
    return *this;
}

void SpreadsheetCell::set(double inValue)
{
    mValue = inValue;
    mString = doubleToString(inValue);
}

void SpreadsheetCell::set(const string& inString)
{
    mString = inString;
    mValue = stringToDouble(inString);
}

double SpreadsheetCell::getValue() const
{
    ++mNumAccesses;
    return mValue;
}

string SpreadsheetCell::getString() const
{
    ++mNumAccesses;
    return mString;
}

string SpreadsheetCell::doubleToString(double inValue)
{
    ostringstream ostr;
    ostr << inValue;
    return ostr.str();
}

double SpreadsheetCell::stringToDouble(string inString)
{
    double temp;
    istringstream istr(inString);
    istr >> temp;
    
    if (istr.fail() || !istr.eof()) 
    {
        return 0;
    }
    return temp;
}

void SpreadsheetCell::setColor(Colors color)
{
    mColor = color;
}

const SpreadsheetCell SpreadsheetCell::operator+(const SpreadsheetCell& cell) const
{
    SpreadsheetCell newCell;
    newCell.set(cell.mValue + mValue);
    return newCell;
}

const SpreadsheetCell SpreadsheetCell::operator+(double rhs) const
{
    return SpreadsheetCell(mValue+rhs);
}

SpreadsheetCell& SpreadsheetCell::operator+=(const SpreadsheetCell& rhs)
{
    set(mValue + rhs.mValue);
    return *this;
}

SpreadsheetCell& SpreadsheetCell::operator-=(const SpreadsheetCell& rhs)
{
    set(mValue - rhs.mValue);
    return *this;
}

SpreadsheetCell& SpreadsheetCell::operator*=(const SpreadsheetCell& rhs)
{
    set(mValue * rhs.mValue);
    return *this;
}

SpreadsheetCell& SpreadsheetCell::operator/=(const SpreadsheetCell& rhs)
{
    if (rhs.mValue == 0)
    {
        throw invalid_argument("Divide by zero.");
    }
    
    set(mValue / rhs.mValue);
    return *this;
}

const SpreadsheetCell SpreadsheetCell::operator-() const
{
    SpreadsheetCell newCell(*this);
    newCell.set(-mValue);
    return newCell;
}

SpreadsheetCell& SpreadsheetCell::operator++()
{
    set(mValue+1);
    return *this;
}

SpreadsheetCell& SpreadsheetCell::operator--()
{
    set(mValue-1);
    return *this;
}

SpreadsheetCell SpreadsheetCell::operator++(int)
{
    SpreadsheetCell oldCell(*this);
    set(mValue + 1);
    return oldCell;
}

SpreadsheetCell SpreadsheetCell::operator--(int)
{
    SpreadsheetCell oldCell(*this);
    set(mValue - 1);
    return oldCell;
}

ostream& operator<<(std::ostream& ostr, const SpreadsheetCell& cell)
{
    ostr<<cell.mString;
    return ostr;
}

istream& operator>>(std::istream& istr, SpreadsheetCell& cell)
{
    string temp;
    istr>>temp;
    cell.set(temp);
    return istr;
}

bool checkSpreadsheetCell(const SpreadsheetCell& cell)
{
    return !(cell.mString.empty());
}

const SpreadsheetCell operator+(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    SpreadsheetCell newCell;
    newCell.set(lhs.mValue + rhs.mValue); // update mValue and mString.
    return newCell;
}

const SpreadsheetCell operator-(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    SpreadsheetCell newCell;
    newCell.set(lhs.mValue - rhs.mValue); // update mValue and mString.
    return newCell;
}

const SpreadsheetCell operator*(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    SpreadsheetCell newCell;
    newCell.set(lhs.mValue * rhs.mValue); // update mValue and mString.
    return newCell;
}

const SpreadsheetCell operator/(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    if (rhs.mValue == 0)
    {
        throw invalid_argument("Divide by zero.");
    }

    SpreadsheetCell newCell;
    newCell.set(lhs.mValue / rhs.mValue); // update mValue and mString.
    return newCell;
}

bool operator==(const SpreadsheetCell& lhs,const SpreadsheetCell& rhs)
{
    return (lhs.mValue == rhs.mValue);
}

bool operator<(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    return (lhs.mValue < rhs.mValue);
}

bool operator>(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    return (lhs.mValue > rhs.mValue);
}

bool operator!=(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    return (lhs.mValue != rhs.mValue);
}

bool operator<=(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    return (lhs.mValue <= rhs.mValue);
}

bool operator>=(const SpreadsheetCell& lhs, const SpreadsheetCell& rhs)
{
    return (lhs.mValue >= rhs.mValue);
}

Spreadsheet.cpp
#include "Spreadsheet.h"

Spreadsheet::Spreadsheet(const SpreadsheetApplication& theApp, 
        int inWidth, int inHeight)
{
    mImpl = new SpreadsheetImpl(theApp, inWidth, inHeight);
}

Spreadsheet::~Spreadsheet()
{
    delete mImpl;
    mImpl = NULL;
}

Spreadsheet::Spreadsheet(const Spreadsheet& src)
{
    mImpl = new SpreadsheetImpl(*(src.mImpl));
}

Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs)
{
    //mImpl = rhs.mImpl; // Incorrect assignment!
    *mImpl = *(rhs.mImpl);
    return *this;
}
    
void Spreadsheet::setCellAt(int x, int y, const SpreadsheetCell& inCell)
{
    mImpl->setCellAt(x, y, inCell);
}

SpreadsheetCell Spreadsheet::getCellAt(int x, int y)
{
    return mImpl->getCellAt(x, y);
}

int Spreadsheet::getId() const
{
    return mImpl->getId();
}


SpreadsheetImpl.cpp
#include "SpreadsheetImpl.h"

int SpreadsheetImpl::sCounter = 0;

SpreadsheetImpl::SpreadsheetImpl(const SpreadsheetApplication& theApp, 
                                int inWidth, int inHeight):
                            mWidth(inWidth<kMaxWidth?inWidth:kMaxWidth),
                            mHeight(inHeight<kMaxHeight?inHeight:kMaxHeight),
                            mTheApp(theApp)
{
    mId = sCounter++;
    mCells = new SpreadsheetCell*[inWidth];
    for(int i =0; i<inWidth; ++i)
    {
        mCells[i] = new SpreadsheetCell[inHeight];
    }
}

SpreadsheetImpl::~SpreadsheetImpl()
{
    for(int i=0; i<this->mWidth; ++i)
    {
        delete []this->mCells[i];
    }
    delete []this->mCells;
    mCells = NULL;
}

SpreadsheetImpl::SpreadsheetImpl(const SpreadsheetImpl& src)
    :mTheApp(src.mTheApp) //must initialized in the initializer list.
{
    mId = sCounter++;
    copyFrom(src);
}

SpreadsheetImpl& SpreadsheetImpl::operator=(const SpreadsheetImpl& rhs)
{
    if(this == &rhs)
    {
        return *this;
    }
    
    for(int i =0; i<this->mWidth; ++i)
    {
        delete []this->mCells[i];
    }
    delete []this->mCells;
    this->mCells = NULL;
    
    copyFrom(rhs);
    return *this;
}

void SpreadsheetImpl::copyFrom(const SpreadsheetImpl& src)
{
    mWidth = src.mWidth;
    mHeight = src.mHeight;
    mCells = new SpreadsheetCell* [mWidth];
    for (int i = 0; i < mWidth; i++) 
    {
        mCells[i] = new SpreadsheetCell[mHeight];
    }
    for (int i = 0; i < mWidth; i++) 
    {
        for (int j = 0; j < mHeight; j++) 
        {
            mCells[i][j] = src.mCells[i][j];
        }
    }
}

void SpreadsheetImpl::setCellAt(int x, int y, const SpreadsheetCell& cell)
{
    if(!inRange(x,mWidth) || !inRange(y, mHeight))
    {
        throw std::out_of_range("");
    }
    mCells[x][y] = cell;
}

SpreadsheetCell SpreadsheetImpl::getCellAt(int x, int y)
{
    if(!inRange(x,mWidth) || !inRange(y, mHeight))
    {
        throw std::out_of_range("");
    }
    return mCells[x][y];
}

bool SpreadsheetImpl::inRange(int val, int upper)
{
    if(val < upper)
    {
        return true;
    }
    return false;
}

int SpreadsheetImpl::getId() const
{
    return mId;
}








發佈了96 篇原創文章 · 獲贊 26 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章