VB.NET - 滥用实例变量?

时间:2013-04-03 18:18:34

标签: vb.net design-patterns

请查看以下代码:

    Public Class A
    Public person1 As Person
End Class

Public Class B
    Inherits A

    Public Function CheckGender() As Boolean
        If person1._Gender = "M" Then
            CheckGender = True
        End If
    End Function

End Class

Public Class C
    Inherits B
    Public Function CheckAge() As Boolean
        If person1._Age > 30 Then
            CheckAge = True
        End If
    End Function

End Class

Public Class D
    Inherits C

    Public Sub SomeMethod()
        Dim list As List(Of Person) = New List(Of Person)
        Dim p1 As New Person("Ian", "M", 31)
        Dim p2 As New Person("Susan", "F", 20)
        Dim p3 As New Person("Mark", "M", 22)
        list.Add(p1)
        list.Add(p2)
        list.Add(p3)

        For Each Person As Person In list
            person1 = Person
            If CheckAge() And CheckGender() Then
                'Do something
            End If
        Next
    End Sub

    Public Shared Sub Main()
        Dim d As New D
        d.SomeMethod()
    End Sub

End Class

Public Class Person
    Public _Name As String
    Public _Gender As String
    Public _Age As String
    Public Sub New(ByVal Name As String, ByVal Gender As String, ByVal Age As Integer)
        _Name = Name
        _Gender = Gender
        _Age = Age
    End Sub
End Class

c.SomeMethod遍历三个人并执行两项检查:b.CheckGenderc.CheckAgeCheckGenderCheckAge使用超类A中的实例变量。

实时环境中的代码每天在数据库中循环100,000条记录,并删除CheckGenderCheckAge都为真的记录。 在这种情况下使用实例变量是一个糟糕的设计选择吗?我总是被教导使用局部变量。我希望Person对象在每个循环中传递给CheckGenderCheckAge。或者它真的没关系?

请注意,上述代码是一个假设的例子。 CheckGenderCheckAge是实际应用程序中的复杂函数。

2 个答案:

答案 0 :(得分:2)

只要CheckGenderCheckAge没有访问层次结构中类的任何私有,受保护或内部成员,并且是公共函数,并且它们的逻辑对于任何实例都是相同的,那么{ {1}},AB,将这些方法放在另一个类中是更好的设计。尽可能使它们静止。您可以让他们接受您的类的最一般实现(例如C),允许检查年龄或性别。从您的代码中获取,您甚至可以传递A属性,而不是使用任何PersonAB类。

在上述情况下使用继承并允许这样的逻辑,只要您需要执行以下任何或所有操作:

  • 符合特定接口或基类CAB类必须实现/扩展,以及该接口或基类提供CCheckGender方法。如果您将对象传递给第三方API,这可以是唯一的解决方案,它接受基类/接口作为参数并在内部调用检查方法。

以下是C#中的示例:

CheckAge
  • 为类提供具体实施。如果public static class CheckUtil { public static bool CheckAgeOfPerson(Person p) { return p.Age > 30; } public static bool CheckAgeOfObject(A obj) { // NOTE: obj.person1 must be accessible - by being either public or internal. // If this class is in another assembly, internal is not useful return CheckAgeOfPerson(obj.person1); } } A objA = ...; B objB = ...; C objC = ...; CheckUtil.CheckAgeOfObject(objA); CheckUtil.CheckAgeOfObject(objB); CheckUtil.CheckAgeOfObject(objC); CheckUtil.CheckAgeOfPerson(objA.person1); CheckUtil.CheckAgeOfPerson(objB.person1); CheckUtil.CheckAgeOfPerson(objC.person1); 的实例需要CheckAge中的某些逻辑,但对A的实例进行完全不同的验证,或B中现有逻辑和某些新逻辑的组合然后继承是你的朋友。不过,如果是这种情况,我宁愿将CCheckGender暴露给接口并通过接口调用它们。这样,只要接口满足,继承就是有效的,但不是强制性的。

这是C#中的一个例子:

CheckAge

对于复杂的情景,也可以使用两种方法的组合(当然,对于比年龄和性别检查更复杂的情况):

public interface IGenderCheckable
{
    bool CheckGender();
}

public interface IAgeCheckable
{
    bool CheckAge();
}

public class A : IAgeCheckable, IGenderCheckable
{
    public virtual bool CheckGender()
    {
        return this.person1.Gender.Equals("M");
    }

    public virtual bool CheckAge()
    {
        return this.person1.Age > 30;
    }
}

public class B : A
{
     public override bool CheckAge()
     {
          // combine custom logic with new logic
          return this.person1.Age < 0 || base.CheckAge();
     }
}

关于实例变量与局部变量的使用 - 在.NET中使用实例变量的性能存在缺陷,特别是当它们是值类型时。例如,public class A : IAgeCheckable, IGenderCheckable { ... } public static class CheckUtil { public static bool CheckAge(IAgeCheckable obj) { return obj.CheckAge(); } public static bool CheckGender(IGenderCheckable) { return obj.CheckGender(); } } 的本地成员的使用被转换为int _someIntMember - 后者又调用堆来获取this._someIntMember对象,然后访问其this成员。将该成员作为局部变量,将其值放入堆栈中,并从那里读取它,而不必通过堆进行不必要的往返。而且,堆栈比堆快。

但是,我不能说过多的堆使用是滥用它,也不能说当它们被过多使用时滥用局部变量。这取决于所需的性能和代码的复杂性。有时局部变量会使代码更具可读性,但如果太多,您可能很容易忘记每个代码是什么(这可能是比疏忽性能增益更严重的问题)。所以这是一个风格和必要性问题。

答案 1 :(得分:2)

如果您有兴趣“修复”您的代码以使Person成为Property而不是字段,请按如下方式更改A类的实现:

Public Class A
    Public Property Person1 As Person

   Public Overridable Function ComputeAge() As Integer
       Return Person1.Age
   End Function

End Class

这里的好处是,如果需要,您可以在获取和设置此属性时添加其他抽象。编译器将为auto属性生成隐藏的私有后备字段。您仍然可以从任何实现类访问Person1:

Public Class B
   Inherits A

   Public Overrides Function ComputeAge() As Integer
       Return MyBase.Person1.Age.AddYears(1)
   End Function
End Class