当要访问的对象被多次封装时,如何正确使用setter?
How to use setters properly when the object to be accessed is encapsulated more than once?
我经常被这个问题困扰,找不到任何明确的解决方案。我想我知道 getters/setters.
的动机
先验信息:
在实现现实生活中的数据时,通常会将数据封装在不止一层。例如:
// 1st stage data types ------------------------------
struct Cartesian
{
int32_t x;
int32_t y;
int32_t z;
}
struct GeoLocation
{
double_t latitude;
double_t longitude;
int32_t altitude;
}
// 2nd stage data types ------------------------------
struct Drone
{
Cartesian baseOffset; // m
Cartesian velocity; // m/s
}
struct Plane
{
GeoLocation location; // semicircle
Cartesian velocity; // knots
}
// 3rd stage data types ------------------------------
struct Swarm
{
Plane base;
Drone member[10];
}
在 C++ 中,我使用 classes 而不是结构(因为为什么不呢?)。当通过某些通信渠道接收到有关 Swarm[3].member[8].velocity.x
的数据时,问题就出现了。意识到一个系统中可以有多个群体。
要求:
根据 MISRA C++ 规则,函数不能 return 对任何 class 成员的非常量引用,因为如果没有 class' [=62=,则不应更改成员].
问题:
当我使用 getter 和 setter 时,我不能说“Swarm[3].member[8].velocity.x
”;相反,我可以用几种方式来表达这一点:
1. 这不允许作为 get() 函数 returns const 引用并且不能调用 set()。
Swarm[3].getMember(8).getVelocity().setX(5)
; (or set("X", 5)
)
2. 这个方法把所有的负担都带进了 Swarm class。虽然对于调用 Swarm 的人来说,代码似乎更短 class,但如果发生变化,代码和后台维护工作非常繁重。
Swarm[3].setMemberVelocity(8,X,5)
3.这种方法有点介于两者之间,但这里的问题是你可能会牺牲效率,因为每次新数据首先到达你创建一个临时变量,得到它, 填充并设置它。
Cartesian tempVelocity = Swarm[3].getMember(8).getVelocity();
tempVelocity.x = 5;
Swarm[3].setMemberVelocity(8, tempVelocity);
这 3 种方法中哪一种最好?或者有什么我可以使用的替代品吗?
提前致谢。
如果它成批到达(如所有群的块),我将 methods/members 将块委托给子对象。
假设您刚刚收到 string update
。您为第一个群拆分数据并调用 swarm[0].update(chunk)
。该方法将进行自己的验证,然后将块拆分为每个成员的信息,并使用较小的部分对成员调用更新。最终你会一直到 Cartesian
这将能够更新 X
和其他一切。
我可能不完全了解 MISRA。但是,它似乎对灵活的设计(而且根本不是 c++ish)适得其反。假设我发现我需要:
struct SuperSwarm {
Swarm swarms[10];
};
然后按照您的选项 2,我需要为所有内部聚合实现大量 setters/getters,因为在您的情况下听起来有点像您需要能够单独设置所有数据。如果您需要更改 Drone
中的某些内容,则会发生同样的事情,那么所有内容都需要更新。对于良好且灵活的设计,您可以看到这真的是一场噩梦。遵守您制定的规则并没有真正好的选择。这就是为什么您通常 return 非常量引用并在不需要特殊处理时使用它的原因。在某些方面,这可以让您的代码面向未来,而不仅仅是一个 public 成员变量。
您可以 'cheat'(并使用您的选项 3)并且在设计方面仍然灵活的一种方法是代理所有内容 - 例如 std::shared_ptr
。当你 return a std::shared_ptr
时,它看起来像是一个副本而且它确实是 - 但它实际上只是同一个指向对象的代理(这类似于其他一些东西在引擎盖下的工作方式OO 编程语言)。不过,您可能需要更合适的代理 class。但又何必呢?为什么不直接使用结构并说它是一个 数据结构 而不是 class 承担(另一个)责任。毕竟那样的话,你的意图就表达的很清楚了。
显然需要简单的旧数据、标准布局和简单可复制的数据。这种数据使得它的二进制布局更容易实现,它是可移植的并且与其他编程语言兼容。 MISRA 要求使用关键字 struct
.
声明此类数据
作为副作用,此类数据还可以编写如下代码:
swarms[swarmNo].drones[droneNo].velocity.x = 5;
那种深链导航仍然被认为是代码味道,它也有名字"train wreck pattern"。然而,它至少没有任何不需要的 get
、set
和 ()
膨胀来输入。
其余所有数据都应遵循面向对象的原则。蜂群本身有责任通过指挥其无人机进行机动,而无人机本身有责任遵循(或拒绝)此类命令。没有什么应该能够简单地从外部设置一些 属性 的东西。
MISRA 要求使用关键字 class
声明此类数据。 public 成员函数不应返回非静态数据成员的私有和非 const 句柄。
我经常被这个问题困扰,找不到任何明确的解决方案。我想我知道 getters/setters.
的动机先验信息:
在实现现实生活中的数据时,通常会将数据封装在不止一层。例如:
// 1st stage data types ------------------------------ struct Cartesian { int32_t x; int32_t y; int32_t z; } struct GeoLocation { double_t latitude; double_t longitude; int32_t altitude; } // 2nd stage data types ------------------------------ struct Drone { Cartesian baseOffset; // m Cartesian velocity; // m/s } struct Plane { GeoLocation location; // semicircle Cartesian velocity; // knots } // 3rd stage data types ------------------------------ struct Swarm { Plane base; Drone member[10]; }
在 C++ 中,我使用 classes 而不是结构(因为为什么不呢?)。当通过某些通信渠道接收到有关 Swarm[3].member[8].velocity.x
的数据时,问题就出现了。意识到一个系统中可以有多个群体。
要求:
根据 MISRA C++ 规则,函数不能 return 对任何 class 成员的非常量引用,因为如果没有 class' [=62=,则不应更改成员].
问题:
当我使用 getter 和 setter 时,我不能说“Swarm[3].member[8].velocity.x
”;相反,我可以用几种方式来表达这一点:
1. 这不允许作为 get() 函数 returns const 引用并且不能调用 set()。
Swarm[3].getMember(8).getVelocity().setX(5)
; (orset("X", 5)
)
2. 这个方法把所有的负担都带进了 Swarm class。虽然对于调用 Swarm 的人来说,代码似乎更短 class,但如果发生变化,代码和后台维护工作非常繁重。
Swarm[3].setMemberVelocity(8,X,5)
3.这种方法有点介于两者之间,但这里的问题是你可能会牺牲效率,因为每次新数据首先到达你创建一个临时变量,得到它, 填充并设置它。
Cartesian tempVelocity = Swarm[3].getMember(8).getVelocity();
tempVelocity.x = 5;
Swarm[3].setMemberVelocity(8, tempVelocity);
这 3 种方法中哪一种最好?或者有什么我可以使用的替代品吗?
提前致谢。
如果它成批到达(如所有群的块),我将 methods/members 将块委托给子对象。
假设您刚刚收到 string update
。您为第一个群拆分数据并调用 swarm[0].update(chunk)
。该方法将进行自己的验证,然后将块拆分为每个成员的信息,并使用较小的部分对成员调用更新。最终你会一直到 Cartesian
这将能够更新 X
和其他一切。
我可能不完全了解 MISRA。但是,它似乎对灵活的设计(而且根本不是 c++ish)适得其反。假设我发现我需要:
struct SuperSwarm {
Swarm swarms[10];
};
然后按照您的选项 2,我需要为所有内部聚合实现大量 setters/getters,因为在您的情况下听起来有点像您需要能够单独设置所有数据。如果您需要更改 Drone
中的某些内容,则会发生同样的事情,那么所有内容都需要更新。对于良好且灵活的设计,您可以看到这真的是一场噩梦。遵守您制定的规则并没有真正好的选择。这就是为什么您通常 return 非常量引用并在不需要特殊处理时使用它的原因。在某些方面,这可以让您的代码面向未来,而不仅仅是一个 public 成员变量。
您可以 'cheat'(并使用您的选项 3)并且在设计方面仍然灵活的一种方法是代理所有内容 - 例如 std::shared_ptr
。当你 return a std::shared_ptr
时,它看起来像是一个副本而且它确实是 - 但它实际上只是同一个指向对象的代理(这类似于其他一些东西在引擎盖下的工作方式OO 编程语言)。不过,您可能需要更合适的代理 class。但又何必呢?为什么不直接使用结构并说它是一个 数据结构 而不是 class 承担(另一个)责任。毕竟那样的话,你的意图就表达的很清楚了。
显然需要简单的旧数据、标准布局和简单可复制的数据。这种数据使得它的二进制布局更容易实现,它是可移植的并且与其他编程语言兼容。 MISRA 要求使用关键字 struct
.
作为副作用,此类数据还可以编写如下代码:
swarms[swarmNo].drones[droneNo].velocity.x = 5;
那种深链导航仍然被认为是代码味道,它也有名字"train wreck pattern"。然而,它至少没有任何不需要的 get
、set
和 ()
膨胀来输入。
其余所有数据都应遵循面向对象的原则。蜂群本身有责任通过指挥其无人机进行机动,而无人机本身有责任遵循(或拒绝)此类命令。没有什么应该能够简单地从外部设置一些 属性 的东西。
MISRA 要求使用关键字 class
声明此类数据。 public 成员函数不应返回非静态数据成员的私有和非 const 句柄。