我想找出设计程序功能的最佳方法。
该程序的主要组成部分是Camera类。此Camera对象表示真实摄像机的程序用户界面,该摄像机通过帧抓取卡与计算机连接。摄像机类可以链接到帧抓取器,启动和停止采集,还可以改变/访问许多不同的摄像机属性。当我说很多时,我说的是250多个独特的命令。通过帧抓取器向物理相机发送串行字符串,向相机发出每个唯一命令。每个命令可以被认为是三种类型之一。动作,查询和值。
动作命令不需要等号,例如“重置”,“打开”,“关闭”
您可以获取但未设置的查询通常与值相关联。例如“温度=?”,“sernum =?”,“maxframerate =?”命令会导致相机发回信息。这些值不能改变,因此“温度= 20”会导致错误。
值是您可以获取和设置的值,通常与值相关联。例如“framerate = 30”和“framerate =?”是两个独特的命令,但我认为基本字符串“framerate”是一个值命令类型,因为它可以被变异和访问。
250个独特的命令可以减少到~100个CameraActions,CameraQuerys和CameraValues。我没有在我的Camera类中使用250个方法,而是想到了编写命令对象而不是单独的setter,getter和actions。命令字符串可以在构造函数中提供,也可以使用setter重置。然后我可以编写一个包含所有可用命令的CameraCommands对象,并将其作为公共成员提供给我的相机。
//CameraAction.h =============================================
class CameraAction {
public:
CameraAction(std::string commandString, SerialInterface* serialInterface);
void operator()() { _serialInterface->sendString(_commandString); }
private:
SerialInterface* _serialInterface;
std::string _commandString;
};
//CameraValue.h =====================================================
class CameraValue {
public:
CameraValue(std::string commandString, double min, double max, SerialInterface* serialInterface);
void set(double value)
{
if(value > _maxValue) { throw std::runtime_error("value too high"); }
if(value < _minValue) { throw std::runtime_error("value too low"); }
std::string valueString = std::to_string(value);
_serialInterface->sendString(_commandString + "=" + valueString);
}
double get()
{
std::string valueString = _serialInterface->sendString(_commandString + "=?");
return atof(valueString.c_str());
}
private:
SerialInterface* _serialInterface;
std::string _commandString;
double _minValue;
double _maxValue;
};
//CameraCommands.h ===================================================
class CameraCommands {
public:
CameraCommands();
CameraAction reset;
CameraQuery temperature;
CameraValue framerate;
CameraValue sensitivity;
//... >100 more of these guys
};
//Camera.h ===========================================================
class Camera {
public:
Camera();
CameraCommands cmd;
void startAcquisition();
void stopAcquisition();
void setDataBuffer(void* buffer);
void setOtherThing(int thing);
};
以便用户可以执行以下操作:
Camera myCamera;
myCamera.cmd.reset();
myCamera.cmd.framerate.set(30);
myCamera.cmd.sensitivity.set(95);
double temperature = myCamera.cmd.temperature.get();
myCamera.startAcquisition();
等...
这里的主要问题是我暴露了公共成员变量,这应该是一个巨大的禁忌。我当前的对象设计是否合乎逻辑,或者我应该简单地实现250个setter和getter以及100个setter和getter来改变最小和最大可设置值。
这对我来说似乎很麻烦,因为还有许多与Camera对象关联的setter / getter与用户命令无关。用户界面提供方法的范围(cmd),以便用户知道相机中是否有物理变异,或者只是在程序对象中进行变异(其他方法),这很好。有没有更好的方法来设计我的程序?
答案 0 :(得分:1)
你基本上描述了一个有趣的层次结构:
Command
- &gt; Query
- &gt; Value
。
Command
保存作为命令文本的字符串;
它还可以为其子女提供protected
Send()
方法。Query
还包含一个(protected
)int
变量(或其他),您可以立即get()
和/或operator int()
或{{} 1}}来自相机; query()
将Value
和/或set()
命令添加到operator =(int)
。 Query
的构造函数(特别是)可以包含Value
和min
。
max
对象可以有多个Camera
成员:
public
通过这样组织,然后:
class Camera {
private: // Classes that no-one else can have!
class Command; friend Command;
#include "Camera.Command.inc"
class Query; friend Query;
#include "Camera.Query.inc"
class Value; friend Value;
#include "Camera.Value.inc"
public: // Variables using above classes
Command reset;
Command open; // Maybe make this one private, for friends?
Command close; // Ditto?
Query temperature;
Query sernum;
Query maxFrameRate;
Value frameRate;
private: // Variables
SerialPort port; // Allow Command and co. access to this
}; // Camera
和query()
方法隐藏了与物理相机连接的机制。您会注意到我在set()
课程的中间添加了#include "Camera.XXX.inc"
。注意:
Camera
扩展程序,因为它们已被“包含”在.inc
文件中:它们并不是唯一的标题文件。答案 1 :(得分:0)
您可以使用一个或多个结构对“设置”进行分组,然后公开一种方法来设置它们:
typedef struct settings{
int setting1;
int setting2;
}MySettings;
class Myclass{
private :
int setting1;
int setting2;
public Myclass(MySettigs *settings)
{
if(null != settings){
setting1=settings->setting1;
setting2=settings->setting2;
}
}
public void ChangeSettings (MySettings *setting){
if(null != settings)
{
setting1=settings->setting1;
setting2=settings->setting2;
}
}
public void TakeSettings (MySettigs *settings){
[copy local variables into the passed struct]
}
我强烈建议在对象处于“可操作”状态时更改设置时要小心。在另一个线程正在使用设置时,您可能会处于未定义状态。
答案 2 :(得分:0)
在你提到的设计中,我不认为通过作文揭露公众成员是一个很大的禁忌。
当暴露公众成员时,最大的禁忌是不安全访问您的班级内部。
一个例子是允许公共访问CameraValue::_maxValue
。用户可以将该值更改为任何内容,从而导致各种未定义的行为。
根据我的设计,我不会拥有CameraCommands
成员,因为从它的外观来看,除了间接水平之外,它不会添加任何其他内容。
我会将所有CameraAction
和CameraValue
成员添加为相机类的一部分,或者继承它们。
这样的事情:
将CameraCommands
合并到Camera
:
class Camera
{
public:
Camera();
CameraAction reset;
CameraQuery temperature;
CameraValue framerate;
CameraValue sensitivity;
//... >100 more of these guys
void startAcquisition();
void stopAcquisition();
void setDataBuffer(void* buffer);
void setOtherThing(int thing);
};
将CameraCommands
继承到Camera
:
class Camera : public CameraCommands
{
public:
Camera();
void startAcquisition();
void stopAcquisition();
void setDataBuffer(void* buffer);
void setOtherThing(int thing);
};
您甚至可以为CameraValue
等提供一些运算符,以便您可以通过赋值(operator=
)设置值,并通过隐式转换(operator T
)或取消引用来获取值(operator*
):
template<typename T>
class CameraValue
{
public
CameraValue(SerialInterface*, std::string cmd);
CameraValue& operator=(const T& val)
{
_val = val;
std::string val_str = std::to_string(_val);
_ser_ifc->sendString(_cmd + "=" + val_str);
}
const T& get() const
{
return _val;
}
// implicit access to _val
operator const T&() const
{
return _val;
}
// dereference operator to access _val
const T& operator*() const
{
return _val;
}
private:
T _val;
SerialInterface* _ser_ifc;
std::string _cmd;
};
然后在课程中使用CameraValue
,如下所示:
using CameraFramerate = CameraValue<int>;
CameraFramerate framerate;
上述技术为(IMO)提供了Camera
的更可组合使用,例如:
Camera camera;
// setting values
camera.framerate = 30;
camera.sensitivity = 95;
// getting values
int framerate = camera.framerate; // uses operator T&()
int framerate = *camera.framerate; // uses operator*()
这里的关键点是Camera::framerate
等不允许任何可能改变您的相机类别的访问权限。内部状态以不确定和/或不安全的方式。