增强'Rock,Paper,Scissors'在VB.NET中包含'Lizard,Spock',使代码更具可扩展性,可维护性和可重用性

时间:2012-12-20 09:06:18

标签: vb.net winforms oop object orm

我是编程和OOP的新手,所以请原谅我缺乏知识。

作为我的Rock,Paper和Scissors游戏的一部分,我有一个抽象的超类(武器),它在VB.NET中有子类( Rock,Paper和Scissors ) :

    Public MustInherit Class Weapons
         Public MustOverride Function compareTo(ByVal Weapons As Object) As Integer

    End Class

    Public Class Paper
        Inherits Weapons

        Public Overrides Function compareTo(ByVal Weapons As Object) As Integer
            If TypeOf Weapons Is Paper Then
                Return 0
            ElseIf TypeOf Weapons Is Rock Then
                Return 1
            Else
                Return -1
            End If
        End Function
    End Class

    Public Class Rock
        Inherits Weapons

        Public Overrides Function compareTo(ByVal Weapons As Object) As Integer
            If TypeOf Weapons Is Rock Then
                Return 0
            ElseIf TypeOf Weapons Is Scissors Then
                Return 1
            Else
                Return -1
            End If
        End Function
    End Class

    Public Class Scissors
        Inherits Weapons

        Public Overrides Function compareTo(ByVal Weapons As Object) As Integer
            If TypeOf Weapons Is Scissors Then
                Return 0
            ElseIf TypeOf Weapons Is Paper Then
                Return 1
            Else
                Return -1
            End If
        End Function
    End Class

还有一个超类播放器,它有子类( PlayerComputerRandom PlayerHumanPlayer PlayerComputerTactical )喜欢:

    Imports RockPaperScissors.Weapons

Public Class Player

    Private pName As String
    Private pNumberOfGamesWon As String
    Public pWeapon As Weapons

    Property Name() As String
        Get
            Return pName
        End Get
        Set(ByVal value As String)
            pName = value
        End Set
    End Property

    Property NumberOfGamesWon As String
        Get
            Return pNumberOfGamesWon
        End Get
        Set(ByVal value As String)
            pNumberOfGamesWon = value
        End Set
    End Property

    Property getWeapon As Weapons
        Get
            Return pWeapon
        End Get
        Set(ByVal value As Weapons)
            pWeapon = value
        End Set
    End Property

    Public Sub pickWeapon(ByVal WeaponType As String)
        If WeaponType = "Rock" Then
            pWeapon = New Rock()

        ElseIf WeaponType = "Paper" Then
            pWeapon = New Paper()

        Else
            pWeapon = New Scissors()

        End If

    End Sub

End Class



    Imports RockPaperScissors.Weapons

Public Class PlayerComputerRandom
    Inherits Player

    Private Enum weaponsList
        Rock
        Paper
        Scissors
    End Enum

    Public Overloads Sub pickWeapon()

        Dim randomChoice = New Random()
        Dim CompChoice As Integer = randomChoice.Next(0, [Enum].GetValues(GetType(weaponsList)).Length)

        If CompChoice = "0" Then
            pWeapon = New Rock()

        ElseIf CompChoice = "1" Then
            pWeapon = New Paper()

        Else
            pWeapon = New Scissors()

        End If


    End Sub

End Class



 Public Class PlayerComputerTactical
    Inherits Player

    Private plastMove As String

    Property lastMove() As String
        Get
            Return plastMove
        End Get
        Set(ByVal value As String)
            plastMove = value
        End Set
    End Property

    Public Overloads Sub pickWeapon()
        ' Add tactical player functionality
    End Sub


End Class


     Public Class PlayerHumanPlayer
        Inherits Player

    End Class

我有GameForm类,它实例化对象并执行用于前端的各种其他逻辑,如下所示:

    Public Class GameForm
    Private Sub btnRock_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRock.Click
        findWinner("HumanPlayer", "Rock", "RandomComputer")
    End Sub

    Private Sub btnPaper_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPaper.Click
        findWinner("HumanPlayer", "Paper", "RandomComputer")
    End Sub


    Private Sub btnScissors_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnScissors.Click
        findWinner("HumanPlayer", "Scissors", "RandomComputer")
    End Sub

    Public Sub findWinner(ByVal p1name As String, ByVal p1WeaponSelected As String, ByVal p2Name As String)
        Dim player1 = New PlayerHumanPlayer()
        Dim player2 = New PlayerComputerRandom()

        player1.Name = p1name
        player1.pickWeapon(p1WeaponSelected)  ' Should I be using the Rock Class???

        player2.Name = p2Name
        player2.pickWeapon()

        Dim winner As Integer = player1.getWeapon().compareTo(player2.getWeapon())

        Select Case winner
            Case 1
                txtGameStatus.Text = player1.Name() + " wins!"
            Case -1
                txtGameStatus.Text = player2.Name() + " wins!"
            Case 0
                txtGameStatus.Text = "Draw!"
        End Select
    End Sub

End Class

我需要做的是能够添加新的武器( Lizard,Spock ),我知道我可以通过简单地添加子类来实现这一点( Lizard,Spock )继承武器基类。

但是,这需要对所有子类( Rock,Paper和Scissors )进行代码更改,这实际上并不是一个长期可维护的解决方案。当然不是最好的做法。

我是编码和OOP的新手所以,有人可以展示如何增强现有的游戏以轻松添加额外的武器吗?我可以使用数据库表来存储武器吗?如果是这样,你能说明一下吗?我只是希望能够为这个游戏提供长期,可重复使用的解决方案。

知道如何实现这一目标吗?任何帮助将不胜感激。

Manys感谢提前

3 个答案:

答案 0 :(得分:1)

虽然可以动态添加新的“子类”但它没有意义。只是看不到“纸”和“摇滚”(例如)作为不同的CLASSES,但作为具有不同属性的同一类。武器的一个属性是“名称”(“摇滚”),另一个属性是它与另一种武器(由名称定义)的比较。

**更新**示例:

Private TheData() As String = {"Scissor|Paper,Spock|Lizard,Rock",
                               "Paper|Rock,Spock|Scissor,Lizard",
                               "Rock|Scissor,Lizard|Paper,Spock",
                               "Spock|Rock,Lizard|Scissor,Paper",
                               "Lizard|Scissor,Paper|Rock,Spock"}

Sub Main()

    Dim Weapons As New List(Of Weapon)

    For Each s In TheData
        Dim spl = s.Split("|"c)
        Weapons.Add(New Weapon(spl(0), spl(1).Split(","c), spl(2).Split(","c)))
    Next

    Dim r As New Random

    Dim outcome(2) As Integer
    For i = 1 To 1000000
        Dim w1 = Weapons(r.Next(Weapons.Count))
        Dim w2 = Weapons(r.Next(Weapons.Count))
        Dim o = w1.CompareTo(w2)
        outcome(o + 1) += 1
    Next i
    Console.WriteLine("Loose = {0}, Win = {1}, Draw = {2}", outcome(0), outcome(2), outcome(1))

    Console.ReadLine()

End Sub

End Module

Public Class Weapon
Implements IComparable(Of Weapon)

Public Name As String
Private StrongerWeapons As List(Of String)
Private WeakerWeapons As List(Of String)

Public Sub New(name As String, stronger As IEnumerable(Of String), weaker As IEnumerable(Of String))
    Me.Name = name
    StrongerWeapons = New List(Of String)(stronger)
    WeakerWeapons = New List(Of String)(weaker)

End Sub

Public Function CompareTo(other As Weapon) As Integer Implements IComparable(Of Weapon).CompareTo
    Select Case True
        Case Me.Name = other.Name : Return 0
        Case WeakerWeapons.Contains(other.Name) : Return -1
        Case StrongerWeapons.Contains(other.Name) : Return 1
        Case Else : Throw New ApplicationException("Error in configuration!")
    End Select
End Function
End Class

现在你将拥有一个可配置的“战斗系统”。

更新后的示例显示系统“正在运行”。 TheData是您的配置“存储”的地方,可以在text / xml文件,数据库或其他任何内容中。

请注意,这是可配置的示例,而不是Stone / Scissor / Paper(Lizard / Spock)的示例,因为在特定情况下,“解决方案”会更简单。< / p>

答案 1 :(得分:0)

还有一件事,如果这有助于你可以为你的类编写运算符以进行比较和其他 例如:

#Region "Operators"
    Public Shared Operator =(ByVal crD1 As GPSCoordinate, ByVal crD2 As GPSCoordinate) As Boolean
        Return IsEql(crD1, crD2)
    End Operator

    Public Shared Operator <>(ByVal crD1 As GPSCoordinate, ByVal crD2 As GPSCoordinate) As Boolean
        Return Not IsEql(crD1, crD2)
    End Operator

    Private Shared Function IsEql(ByVal crD1 As GPSCoordinate, ByVal crD2 As GPSCoordinate) As Boolean
        If crD1 Is Nothing And crD2 Is Nothing Then
            Return True
        ElseIf Not crD1 Is Nothing And Not crD2 Is Nothing Then
            Return CBool(crD1.Value = crD2.Value)
        End If
        Return False
    End Function
#End Region

答案 2 :(得分:0)

广泛使用的方法是double dispatching。你应用这个whern你需要定义一个依赖于两个不同类的行为(或返回值)。您不是创建switch语句,而是为每个案例创建一条消息,并让每个类决定如何操作。我不熟悉VB,所以请原谅我使用其他语言,但我想你会明白这个想法:

abstract class Weapon
{
abstract public function compareTo($otherWeapon);
abstract public function compareToRock();
abstract public function compareToPaper();
}

class Rock extends Weapon
{
public function compareTo($otherWeapon)
{
return $otherWeapon->compareToRock();
}

public function compareToRock(){return 0;}

public function compareToPaper(){return -1;}
}

class Paper extends Weapon
{
public function compareTo($otherWeapon)
{
return $otherWeapon->compareToPaper();
}

public function compareToRock(){return 1;}

public function compareToPaper(){return 0;}
}

下一步是添加Scissors类,这意味着:

  • 在超类中添加compareToScissors()抽象消息。
  • 在每个子类中添加compareToScissors()实现。
  • 添加Scissors类并实施对应方法。

添加LizardSpock只是重复相同的步骤。正如你所看到的,这里有一些权衡:

  • (+)您正在添加行为,而不是更改现有行为(即您没有修改现有方法实现)。从维护和测试的角度来看,这是很好的(您的测试应该仍然有效)。
  • (+)这更多地取决于个人品味,但对于我来说,用单一方法分隔switch语句更容易理解。
  • ( - )有方法爆炸。这是双重调度的众所周知的副作用,并且添加新变体意味着在所有其他类中添加新方法。

作为最后一个注释,您可以考虑不返回整数,而是将结果实际建模为对象(Win / Loose / Tie)。通过这样做,您可以将行为委托给结果,而不是在其上创建switch语句。

HTH