"Getter/Setter中的价值验证"是一种好的风格吗?

Cha*_*rly 5 c++ getter setter exception

我的Getter/Setter方法在设置/返回之前检查该值.当值无效时,它们会抛出异常(BadArgumentException或IllegalStateException).这是必需的,因为我们使用无效值初始化所有成员 - 因此我们避免使用这些无效值(==在其他地方获取错误/段错误/异常).

好处是:

  • 从模型中收到成员值时,您知道它们是有效的
  • 有效性检查仅在模型对象中执行
  • 值范围在model-object中定义

这似乎是非常不寻常的,因为大多数新团队成员首先抱怨它 - 即使他们在向我们解释之后同意我的意见.

问题:这是一种很好的编程风格吗?(即使它浪费了一点性能)

示例代码:

inline bool MyClass::HasGroupID const { return m_iGroupID != 0; }

int MyClass::GetGroupID() const
{
    if( !HasGroupID() )
        throw EPTIllegalStateException( "Cannot access uninitialized group ID!" );

    return m_iGroupID;

}   // END GetGroupID() const

void MyClass::SetGroupID( int iGroupID )
{
    // negative IDs are allowed!
    if( iGroupID != 0 )
        throw EPTBadArgumentException( "GroupID must not be zero!", iGroupID );

    m_iGroupID = iGroupID;

}   // END SetGroupID( int )
Run Code Online (Sandbox Code Playgroud)

用法:

// in serialization
if( myObject.HasGroupID() )
    rStream.writeAttribute( "groupid", GetGroupID() );

// in de-serialization
try {
    // required attribute - throw exception if value is invalid
    myObject.SetGroupID( rStream.GetIntegerAttribute( "groupid" );
} catch( EPTBadArgumentException& rException )
{
    throw EPTBadXmlAttribute( fileName, rException );
}
Run Code Online (Sandbox Code Playgroud)

Fre*_*abe 5

我认为这是常见的风格,而且它肯定是第一个想法中 getter/setter 的核心动机之一。但是,在您的特定示例中,我认为它们没有意义,而且我认为通常有更好的选择:

如果调用GetGroupIDHasGroupIDyield false 时引发异常,那么这是一个程序员错误 - 所以你可能应该使用assert. 事实上,为什么一开始就可以有一个没有组 ID 的对象?这听起来有点不雅的设计。

此外,如果只有非零 ID 有效,那么您应该尝试使用专用类型更早地强制执行此限制。这样,所有处理组 ID 的代码都可以确保 ID 有效(实际上,编译器会强制执行此操作!)并且您不可能忘记检查某个地方。考虑:

class GroupID {
public:
    explicit GroupID( int id ) : m_id( id ) {
        if ( m_id == 0 ) {
            throw EPTBadArgumentException( "GroupID must not be zero!", m_id );
        }
    }

    // You may want to allow implicit conversion or rather use an explicit getter
    operator int() const { return m_id; }
};
Run Code Online (Sandbox Code Playgroud)

使用这个,你可以写

void MyClass::SetGroupID( GroupID id )
{
    // No error checking needed; we *know* it's valid, because it's a GroupID!
    // We can also use a less verbose variable name without losing readability
    // because the type name reveals the semantics.
    m_iGroupID = id;

} // END SetGroupID( int )
Run Code Online (Sandbox Code Playgroud)

事实上,由于组 ID 现在是一种专用类型,您可以将函数重命名为 justSet并使用重载。所以,你必须MyClass::Set(UserID)MyClass::Set(GroupID)等。

  • @Charly:处理带有异常的编程错误听起来很像*我们不解决错误,我们将它们推到地毯下并希望它们不会爬出来*。断言和异常之间的区别在于,异常可以被捕获和忽略,但永远不应忽略编程错误。您应该重新考虑断言的使用。 (2认同)