全局变量还不好吗?

时间:2009-09-01 21:06:21

标签: c# vb.net visual-studio

今天我跑过这样的代码;具有对整个类可见的流控制标志。我的直觉说这是处理流量控制需求的错误方法(它几乎就像C中的旧时全局标志)。

(示例在VB.Net中,但类似的事情可以在C#中完成。)

Public Class myClass

    Private bMyFlag As Boolean ''// <------

    private sub New
        bMyFlag = false
    end sub

    private sub mySub1
        bMyFlag = true
        mySub3()
        mySub4()
        mySubN()
    end sub

    private sub mySub2
        bMyFlag = false
        mySub3()
        mySub4()
        mySubN()
    end sub

    private sub mySub3
        if bMyFlag then
            DoSomething()
        else
            DoSomethingElse()
        end if
    end sub

    private sub mySub4
        if not bMyFlag then
            DoSomethingDifferent()
        else
            DoSomethingReallyDifferent()
        end if
    end sub

    private sub mySubN
        if not bMyFlag then
            DoNotPassGo()
        else
            DoNotCollect200Dollars()
        end if
    end sub
end class

对我来说很明显,它是在事后嫁接的。我正计划重新编写代码,以便bMyFlag是传递给mySub3,mySub4和mySuvN的参数。但是,在我这样做之前:

是否有合理的理由让流控制标志对于类是全局的?

如果没有,为什么这被认为是不好的做法?

8 个答案:

答案 0 :(得分:12)

这不是一个全局(即应用程序范围)变量,它是实例级别(即作用于该类)。没有任何问题,所以这就是预期的。

因此当该标志为真时,如果整个实例都为真,那就应该是它。如果每次都以完全不同的方式使用某些通用的东西,则不应在那里声明它。

答案 1 :(得分:9)

我不认为这真的是“全球性的”。它只是一个成员级别的领域。在大多数情况下,这是完全可以接受的(但不是真正的流量控制 - 看起来你真正需要的是一些类重新设计)。

答案 2 :(得分:7)

在这种情况下,bMyFlag似乎引入了不必要的状态,这可能使开发复杂化(调试等)。某些子例程会修改其值,而其他子例程则根据其当前值执行不同的操作。这似乎带来了不必要的复杂性,可以通过一些重构来减少。为什么不直接将布尔值传递给mySub1mySub2,然后让这些子程序将该值作为参数调用其他函数?

答案 3 :(得分:4)

很难从一个人为的例子中看出来,但你可能想要考虑引入一个策略模式来处理基于对象状态的不同行为。通过这种方式,您可以将不同的行为实现为可互换的策略。这些策略将根据对象的状态进行交换。每种方法都会简单地为其行为调用当前策略。当然,如果战略复杂或众多,我只会这样做。

对于某些合理的东西,你也可以使用继承,例如,我有两种状态:只读和可编辑。锁定对象将获取Editable对象并返回该对象的只读版本。实际上,只有一个对象,但是它周围有可编辑和只读的包装器。行为的差异内置于包装器中(如果您尝试更改某些内容,则readonly对象会抛出异常)。

如果它非常简单并且不是普遍存在的话,我认为保持它的方式没有任何实际问题。最终if / else的东西会变得复杂和丑陋,然后你可以重构一些更清洁的东西,如上所述。

根据您的评论

更新

听起来你有一个可以通过继承解决的问题。你想要一个LoggableObject。此LoggableObject有两种不同的变体:ScreenLoggable和FileLoggable。为ILoggable定义一个接口,并为包含任何公共代码的抽象基类定义。然后定义一个写入屏幕的ScreenLoggable类,它应该从您的基类继承并包含写入屏幕的代码变体。定义写入文件的FileLoggable类。它还应该从您的基类继承,但包含写入文件的变体。使用工厂根据您的标志创建所需的ILoggable类型。根据界面使用它,做正确的事。

答案 4 :(得分:2)

毕竟,对象是数据+操作,在您的示例中,bMyFlag只是一些对象数据(状态)。如果其他方法不使用bMyFlag,而只使用mySub3mySub4mySubN,则可以将其作为参数。否则,保持原样是合理的,如果bMyFlag封装了实例的某些有用状态,则{{1}}是该类的有效实例成员。

答案 5 :(得分:1)

有一些内部变量来存储状态是可以的,但只有(在我看来)你希望你的对象暴露一个状态的接口。在这种情况下,私有方法使用它,这对我来说只是糟糕的设计。

答案 6 :(得分:1)

如果您的示例代码反映了真实代码,我会说使用标志成员是一种不好的做法。 Mysub3,mysub4等真的做了两件不同的事情,所以它们应该是不同的方法。因此根本不需要旗帜。

答案 7 :(得分:0)

它不是全球性的,但它仍然是凌乱的。如果只使用了几次,那么为了简单起见也许可以放弃,但任何超过2或3次&amp;你应该寻求将不同的功能提取到其他类中。

如果要将此类的所有代码保留在本类中,则可以将类嵌套在其中。有些事情如下(抱歉,如果vb语法错误,已经有一段时间了)

Public Class myClass

Private bMyobj As iThing ' <------

Private Sub New()
    bMyobj = null
End Sub

Private Sub mySub1()
    bMyobj = New Thing2()
    mySub3()
    mySub4()
    mySubN()
End Sub

Private Sub mySub2()
    bMyobj = New Thing1()
    mySub3()
    mySub4()
    mySubN()
End Sub

Private Sub mySub3()
    bMyObj.DoSomething()
End Sub

Private Sub mySub4()
    bMyObj.DoSomethingDifferent()
End Sub

Private Sub mySubN()
    bMyObj.PassGo()
End Sub


 Public Interface iThing
     Sub DoSomething()
     Sub DoSomethingDifferent()
     Sub PassGo()
 End Interface

 Public Class Thing1
     Implements iThing

     Public Sub DoSomething()
         ' Implementation 1
     End Sub
     Public Sub DoSomethingDifferent()
         ' Implementation 1
     End Sub
     Public Sub PassGo()
         ' Don't do it
     End Sub
 End Class
 Public Class Thing2
     Implements iThing
     Public Sub DoSomething()
         ' Implementation 2
     End Sub
     Public Sub DoSomethingDifferent()
         ' Implementation 2
     End Sub
     Public Sub PassGo()
         ' Don't collect 200 dollars
     End Sub
 End Class

End Class