2009-09-01 54 views
3

我今天遇到了類似這樣的代碼;具有對整個班級可見的流量控制標誌。我的直覺告訴我們,這是處理流量控制需求的錯誤方法(它幾乎就像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的參數。但是,在我這樣做之前:

是否有一個有效的理由讓流控制標誌對類是全局的?

如果不是,爲什麼這被認爲是不好的做法?

+1

我不打算髮佈一個完整的答案,但你應該瞭解得墨忒耳定律:HTTP:// EN .wikipedia.org/wiki/Law_of_Demeter – 2009-09-01 21:09:47

回答

7

在這種情況下,bMyFlag似乎正在引入不必要的狀態,這可能會使開發(調試等)複雜化。一些子程序修改了它的值,其他的子程序根據它的當前值執行不同的操作。這似乎引入了不必要的複雜性,可以通過一些重構來減少複雜性。爲什麼不傳遞一個布爾值到mySub1mySub2,然後讓這些子程序調用你的其他函數作爲參數?

+2

是的,將一個布爾值明確地傳遞給mySub1和mySub2是我的計劃 – BIBD 2009-09-01 21:19:27

+0

er。,make mySub3,mySub4和mySubN – BIBD 2009-09-01 21:31:45

+0

看起來像一個方法設置狀態並執行一些init,另一個方法設置不同的狀態並執行其他初始化。然後對象的行爲相應。幾乎不值得。 (根據發佈的代碼。) – snarf 2009-09-01 23:23:27

9

我不認爲這真的是一個「全球」。它只是一個成員級別的字段。在大多數情況下,這是完全可以接受的(但不是真正的流量控制 - 看起來你真正需要的是重新設計一些類)。

12

這不是全局變量(即應用程序範圍)變量,它是實例級別(即範圍限於類)沒有任何問題,只要這是預期的。

所以當這個標誌爲真時,如果它對於整個實例是真的,那就是它應該在的地方。如果每次都以完全不同的方式使用某種通用的東西,那麼就不應該在那裏聲明它。

+4

我認爲你忽略了甜甜圈提到的事實 - 2種方法根據標誌的值做不同的事情。那,恕我直言,這裏是最大的味道。 – 2009-09-01 21:29:18

+0

考慮到psudo代碼的通用性,我認爲很難說明任何內容。雖然甜甜圈可能是正確的,但可能有依賴於該狀態持續存在的方法。沒有進一步澄清,我認爲不可能明確地說這是做對事情的正確或錯誤的方式。 – 2009-09-01 23:15:47

+0

根據發佈的「模糊」代碼,類字段可能(可能是?)影響其行爲的對象狀態(即類似「緩存數據」或「適用於所有用戶」)。也許有些東西可以更好地抽象出來,但它看起來並不荒謬或可怕。 – snarf 2009-09-01 23:20:25

2

There are很多時候,當它是有效的有你指的模式。畢竟,對象是數據+操作,在你的例子中bMyFlag只是一些對象數據(狀態)。如果其他方法不使用bMyFlag,並且只有mySub3,mySub4,mySubN這樣做,那麼你可以使它成爲它們的一個參數。否則,按原樣離開它是合理的,如果它封裝了實例的某個有用狀態,則bMyFlag是該類的有效實例成員。

1

有一些內部變量來存儲狀態是好的,但只有(在我看來)當你想讓你的對象暴露一個接口的狀態。在這種情況下,它被私有方法使用,這對我來說只是不好的設計。

1

如果您的示例代碼反映了真實的代碼,我會說使用標誌成員是一種不好的做法。 Mysub3,mysub4等實際上是在做兩件不同的事情,所以它們應該是不同的方法。因此根本不需要旗幟。

+0

在mySub3和mySub4中還有更多的東西正在進行中,我簡化了它。就像在我們記錄到文本文件的情況下,而另一個我們正在將消息扔到屏幕上。 – BIBD 2009-09-01 21:25:36

+0

是的,但是然後你必須有一個外部實體根據對象的狀態來決定調用哪個方法。對我而言,這比實施的方式更糟糕。你會在不需要的類之間引入耦合。我認爲,班級應該根據自己的狀態知道如何獨立行事。問題在於最簡單和最乾淨的方法是什麼。不過,它們不一定是相同的。 – tvanfosson 2009-09-01 21:26:34

+1

@CodeSlave - 基於你的評論,這聽起來像你應該爲此使用繼承。你有一種登錄到數據庫的對象和一種登錄到屏幕的對象。它們應該從相同的基類中派生出來,但是你應該創建你需要的類。我不會將該標誌引入方法參數。 – tvanfosson 2009-09-01 21:28:22

4

很難從一個人爲的例子說,但你可能要考慮引入戰略模式來處理基於對象的狀態的不同行爲。通過這種方式,你可以實現不同的行爲作爲可互換的策略。這些策略將根據對象的狀態交換進/出。每種方法都會簡單地爲其行爲調用當前策略。當然,如果策略複雜或衆多,我只會這樣做。

對於某些合理簡單的你也可以使用繼承,例如,我有兩種狀態:只讀和可編輯。鎖定對象需要一個Editable對象並返回該對象的只讀版本。實際上,只有一個對象,但是你有可編輯和只讀的包裝。行爲的差異被內置到包裝中(如果您嘗試更改某些內容,只讀對象會引發異常)。

如果它非常簡單,並且沒有普及,我沒有看到任何真正的問題,保持它的方式。最終if/else的東西會變得複雜和醜陋,那麼你可以重構更乾淨的東西,就像我上面所描述的那樣。

更新根據您的意見:

聽起來真是你有一個問題是可解與繼承。你想要一個LoggableObject。這個LoggableObject有兩個不同的變體:ScreenLoggable和FileLoggable。爲ILoggable和一個包含任何公共代碼的抽象基類定義一個接口。然後定義一個寫入屏幕的ScreenLoggable類,它應該從您的基類繼承,幷包含寫入屏幕的代碼的變體。定義寫入文件的FileLoggable類。它也應該從您的基類繼承,但包含寫入文件的變體。根據您的標誌使用工廠創建您需要的ILoggable類型。根據界面使用它,它會做正確的事情。

+0

我對有關日誌記錄的評論只是可能發生的一個例子。但是,注意到和+1。 – BIBD 2009-09-01 21:40:03

0

這不是全球性的,但它仍然凌亂。如果它只用了幾次,那麼也許它可以簡單地離開,但是任何超過2或3倍的東西,你應該將不同的功能提取到其他類中。

如果你想保持所有的代碼都是本地的,你可以在其中嵌套類。像下面的(對不起,如果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 
+0

這是國家模式,正是我所想的。 (但是有一個錯字:在bMyFlag中,我認爲你的意思是「bMyobj =」而不是「bMyFlag =」。) – Anon 2009-09-01 22:23:48

+0

(在mySub2()中,而是糾正我自己的錯字)。 – Anon 2009-09-01 23:07:17

+0

不能相信這沒有得票。我沒有註冊,所以不能投票,但精神+1!如果實際要求只是 - 或者沒有保存狀態的記錄,那當然是完全不同的,但是根據問題中顯示的代碼保存和切換狀態,我最喜歡這個答案。 – Anon 2009-09-03 15:37:14