我在思考更加面向对象的方法,使用
状态模式
:
这台机器:
#pragma once
#include "MachineStates.h"
class AbstractState;
class Machine {
friend class AbstractState;
public:
Machine(unsigned int _stock);
void sell(unsigned int quantity);
void refill(unsigned int quantity);
unsigned int getStock();
~Machine();
private:
unsigned int stock;
AbstractState *state;
};
#include "Machine.h"
#include "MachineStates.h"
Machine::Machine(unsigned int _stock) {
stock = _stock;
state = _stock > 0 ? static_cast<AbstractState *>(new Normal())
: static_cast<AbstractState *>(new SoldOut());
}
Machine::~Machine() { delete state; }
void Machine::sell(unsigned int quantity) { state->sell(*this, quantity); }
void Machine::refill(unsigned int quantity) { state->refill(*this, quantity); }
unsigned int Machine::getStock() { return stock; }
美国各州:
#pragma once
#include "Machine.h"
#include <exception>
#include <stdexcept>
class Machine;
class AbstractState {
public:
virtual void sell(Machine &machine, unsigned int quantity) = 0;
virtual void refill(Machine &machine, unsigned int quantity) = 0;
virtual ~AbstractState();
protected:
void setState(Machine &machine, AbstractState *st);
void updateStock(Machine &machine, unsigned int quantity);
};
class Normal : public AbstractState {
public:
virtual void sell(Machine &machine, unsigned int quantity);
virtual void refill(Machine &machine, unsigned int quantity);
virtual ~Normal();
};
class SoldOut : public AbstractState {
public:
virtual void sell(Machine &machine, unsigned int quantity);
virtual void refill(Machine &machine, unsigned int quantity);
virtual ~SoldOut();
};
#include "MachineStates.h"
AbstractState::~AbstractState() {}
void AbstractState::setState(Machine &machine, AbstractState *state) {
AbstractState *aux = machine.state;
machine.state = state;
delete aux;
}
void AbstractState::updateStock(Machine &machine, unsigned int quantity) {
machine.stock = quantity;
}
Normal::~Normal() {}
void Normal::sell(Machine &machine, unsigned int quantity) {
unsigned int currStock = machine.getStock();
if (currStock < quantity) {
throw std::runtime_error("Not enough stock");
}
updateStock(machine, currStock - quantity);
if (machine.getStock() == 0) {
setState(machine, new SoldOut());
}
}
void Normal::refill(Machine &machine, unsigned int quantity) {
int currStock = machine.getStock();
updateStock(machine, currStock + quantity);
}
SoldOut::~SoldOut() {}
void SoldOut::sell(Machine &machine, unsigned int quantity) {
throw std::runtime_error("Sold out!");
}
void SoldOut::refill(Machine &machine, unsigned int quantity) {
updateStock(machine, quantity);
setState(machine, new Normal());
}
我不习惯使用C++编程,但是这段代码明显可以在GCC 4.8.2上编译通过,同时在
clang
@
11.0.0
和Valgrind测试中也没有内存泄漏问题,所以我认为它是可行的。虽然我不涉及货币计算,但我并不需要这个来展示我的想法。
测试方法如下:
#include "Machine.h"
#include "MachineStates.h"
#include <iostream>
#include <stdexcept>
int main() {
Machine m(10), m2(0);
m.sell(10);
std::cout << "m: "
<< "Sold 10 items" << std::endl;
try {
m.sell(1);
} catch (std::exception &e) {
std::cerr << "m: " << e.what() << std::endl;
}
m.refill(20);
std::cout << "m: "
<< "Refilled 20 items" << std::endl;
m.sell(10);
std::cout << "m: "
<< "Sold 10 items" << std::endl;
std::cout << "m: "
<< "Remaining " << m.getStock() << " items" << std::endl;
m.sell(5);
std::cout << "m: "
<< "Sold 5 items" << std::endl;
std::cout << "m: "
<< "Remaining " << m.getStock() << " items" << std::endl;
try {
m.sell(10);
} catch (std::exception &e) {
std::cerr << "m: " << e.what() << std::endl;
}
try {
m2.sell(1);
} catch (std::exception &e) {
std::cerr << "m2: " << e.what() << std::endl;
}
return 0;
}
一点点的Makefile
:
CC = clang++
CFLAGS = -g -Wall -std=c++17
main: main.o Machine.o MachineStates.o
$(CC) $(CFLAGS) -o main main.o Machine.o MachineStates.o
main.o: main.cpp Machine.h MachineStates.h
$(CC) $(CFLAGS) -c main.cpp
Machine.o: Machine.h MachineStates.h
MachineStates.o: Machine.h MachineStates.h
clean:
$(RM) main
然后运行:
make main
./main
输出结果为:
m: Sold 10 items
m: Sold out!
m: Refilled 20 items
m: Sold 10 items
m: Remaining 10 items
m: Sold 5 items
m: Remaining 5 items
m: Not enough stock
m2: Not enough stock
现在,如果您想添加一个“Broken”状态,您只需要另一个“AbstractState”子类即可:
diff --git a/Machine.cpp b/Machine.cpp
index 935d654..6c1f421 100644
--- a/Machine.cpp
+++ b/Machine.cpp
@@ -13,4 +13,8 @@ void Machine::sell(unsigned int quantity) { state->sell(*this, quantity); }
void Machine::refill(unsigned int quantity) { state->refill(*this, quantity); }
+void Machine::damage() { state->damage(*this); }
+
+void Machine::fix() { state->fix(*this); }
+
unsigned int Machine::getStock() { return stock; }
diff --git a/Machine.h b/Machine.h
index aa983d0..706dde2 100644
--- a/Machine.h
+++ b/Machine.h
@@ -12,6 +12,8 @@ public:
Machine(unsigned int _stock);
void sell(unsigned int quantity);
void refill(unsigned int quantity);
+ void damage();
+ void fix();
unsigned int getStock();
~Machine();
diff --git a/MachineStates.cpp b/MachineStates.cpp
index 9656783..d35a53d 100644
--- a/MachineStates.cpp
+++ b/MachineStates.cpp
@@ -13,6 +13,16 @@ void AbstractState::updateStock(Machine &machine, unsigned int quantity) {
machine.stock = quantity;
}
+void AbstractState::damage(Machine &machine) {
+ setState(machine, new Broken());
+};
+
+void AbstractState::fix(Machine &machine) {
+ setState(machine, machine.stock > 0
+ ? static_cast<AbstractState *>(new Normal())
+ : static_cast<AbstractState *>(new SoldOut()));
+};
+
Normal::~Normal() {}
void Normal::sell(Machine &machine, unsigned int quantity) {
@@ -33,6 +43,10 @@ void Normal::refill(Machine &machine, unsigned int quantity) {
updateStock(machine, currStock + quantity);
}
+void Normal::fix(Machine &machine) {
+ throw std::runtime_error("If it ain't broke, don't fix it!");
+};
+
SoldOut::~SoldOut() {}
void SoldOut::sell(Machine &machine, unsigned int quantity) {
@@ -43,3 +57,17 @@ void SoldOut::refill(Machine &machine, unsigned int quantity) {
updateStock(machine, quantity);
setState(machine, new Normal());
}
+
+void SoldOut::fix(Machine &machine) {
+ throw std::runtime_error("If it ain't broke, don't fix it!");
+};
+
+Broken::~Broken() {}
+
+void Broken::sell(Machine &machine, unsigned int quantity) {
+ throw std::runtime_error("Machine is broken! Fix it before sell");
+}
+
+void Broken::refill(Machine &machine, unsigned int quantity) {
+ throw std::runtime_error("Machine is broken! Fix it before refill");
+}
diff --git a/MachineStates.h b/MachineStates.h
index b117d3c..3921d35 100644
--- a/MachineStates.h
+++ b/MachineStates.h
@@ -11,6 +11,8 @@ class AbstractState {
public:
virtual void sell(Machine &machine, unsigned int quantity) = 0;
virtual void refill(Machine &machine, unsigned int quantity) = 0;
+ virtual void damage(Machine &machine);
+ virtual void fix(Machine &machine);
virtual ~AbstractState();
protected:
@@ -22,6 +24,7 @@ class Normal : public AbstractState {
public:
virtual void sell(Machine &machine, unsigned int quantity);
virtual void refill(Machine &machine, unsigned int quantity);
+ virtual void fix(Machine &machine);
virtual ~Normal();
};
@@ -29,5 +32,13 @@ class SoldOut : public AbstractState {
public:
virtual void sell(Machine &machine, unsigned int quantity);
virtual void refill(Machine &machine, unsigned int quantity);
+ virtual void fix(Machine &machine);
virtual ~SoldOut();
};
+
+class Broken : public AbstractState {
+public:
+ virtual void sell(Machine &machine, unsigned int quantity);
+ virtual void refill(Machine &machine, unsigned int quantity);
+ virtual ~Broken();
+};
diff --git a/main b/main
index 26915c2..de2c3e5 100755
Binary files a/main and b/main differ
diff --git a/main.cpp b/main.cpp
index 8c57fed..82ea0bf 100644
--- a/main.cpp
+++ b/main.cpp
@@ -39,11 +39,34 @@ int main() {
std::cerr << "m: " << e.what() << std::endl;
}
+ m.damage();
+ std::cout << "m: "
+ << "Machine is broken" << std::endl;
+ m.fix();
+ std::cout << "m: "
+ << "Fixed! In stock: " << m.getStock() << " items" << std::endl;
+
try {
m2.sell(1);
} catch (std::exception &e) {
std::cerr << "m2: " << e.what() << std::endl;
}
+ try {
+ m2.fix();
+ } catch (std::exception &e) {
+ std::cerr << "m2: " << e.what() << std::endl;
+ }
+
+ m2.damage();
+ std::cout << "m2: "
+ << "Machine is broken" << std::endl;
+
+ try {
+ m2.refill(10);
+ } catch (std::exception &e) {
+ std::cerr << "m2: " << e.what() << std::endl;
+ }
+
return 0;
}
为了添加更多的产品,你需要有一个包含产品及其库存数量等信息的产品地图...
最终代码可以在
this repo中找到。