OO Pattern: совместная работа между абстрактным базовым классом и подклассами - PullRequest
4 голосов
/ 26 февраля 2010

У меня есть абстрактный базовый класс T, от которого наследуются классы A и B. Теперь у меня есть операция (на T), которая требует немного другой реализации в A и B, но большая часть кода одинакова. Позвольте мне привести пример: есть две возможности реализовать что-то вроде .Clone метода:

Public MustInherit Class T
    Protected MustInherit Function _DoClone() As T

    Public Function Clone() As T
        Dim clone = Me._DoClone()    ' do the subclass-specific stuff '
        ... ' do the shared stuff '
    End Function
End Class

Public Class A
    Inherits T

    Protected Overrides Function _DoClone() As T
        ... ' do the subclass-specific stuff '
    End Function
End Class

или

Public MustInherit Class T
    Protected Sub _DoClone(clone As T)
        ... ' do the shared stuff '
    End Function

    Public MustInherit Function Clone() As T
End Class

Public Class A
    Inherits T

    Public Overrides Function Clone() As T
        Dim clone = ... ' do the subclass-specific stuff '
        Me._DoClone(clone)
    End Function
End Class

(Пример приведен в VB.NET, но тот же вопрос относится к C #, Java и т. Д.)

Мои вопросы:

  • Есть ли вариант, который явно предпочтительнее?
  • Является ли это хорошо известным шаблоном с именем (чтобы я мог провести дополнительные исследования)?
  • Существуют ли общепринятые соглашения об именах для подобных ситуаций (то есть для _Do...)?

Ответы [ 4 ]

7 голосов
/ 26 февраля 2010

выглядит как Шаблонный метод pattern:

Шаблонный метод используется для:

  • пусть подклассы реализуют (с помощью переопределения методов) поведение, которое может варьироваться
  • избежать дублирования в коде: вы ищете общий код в алгоритме и реализуете варианты в подклассах
  • контроль, в какой точке разрешено создание подклассов.
2 голосов
/ 26 февраля 2010

Если для работы клонирования требуется код в базовом классе, первый вариант является правильным. Второй вариант позволяет реализовать метод Clone без вызова кода в базовом классе.

Если код в базовом классе не требуется, второй вариант является правильным. Первый вариант не позволяет реализовать метод Clone без вызова кода в базовом классе.

1 голос
/ 26 февраля 2010

Первый вариант достаточно справедлив, когда есть конкретная работа, которая, как вы знаете, будет отличаться в разных производных классах. Возможно, вы хотите иметь

Public MustInherit Class T
    Protected MustInherit Function Clone() As T

    Public Function CloneAdjusted() As T
        Dim clone = Me.DoClone()
        ' Adjust clone '
    End Function
End Class

Public Class A
    Inherits T

    Protected Overrides Function Clone() As T
        ' Make exact copy of A '
    End Function
End Class

Второй подход был бы более уместным, если вы просто хотите клонировать любой старый T, но хотите внести небольшие изменения в подклассы. Но в этом случае переопределите метод Clone, а не создавайте новый с новым именем. Например

Public MustInherit Class T
    Public Overridable Function Clone() As T
        Dim clone = ' Clone a T here '
    End Function
End Class

Public Class A
    Inherits T

    Public Overrides Function Clone() As T
        ' Any pre-clone work '
        T obj = MyBase.Clone()
        ' Any post-clone work '
    End Function
End Class

И нет, это не имеет смысла для клона, когда вы смотрите на него, потому что вы собираетесь получить обратно T от базового клона, и преобразование этого в A не все легко.

Есть несколько других вариантов, которые вы могли бы рассмотреть.

Один из них - вспомогательный класс. Вместо того, чтобы иметь какой-либо код на T, вы можете захотеть переместить свой общий код в статический метод статического класса, чтобы избежать дублирования.

Другой вариант - наличие интерфейса ICloner, для которого T понимает контракт, но A и B понимают реализацию, например

Public MustInherit Class T
    Protected ICloner Cloner

    Public Overridable Function Clone() As T
        ' Common Code '
        Dim clone = Cloner.GetClone()
        ' More Common Code '
    End Function
End Class

Public Class A Inherits T
    Public Sub New()
        Cloner = New ACloner(Me)
    End Sub
End Class

Достаточно смущен?

1 голос
/ 26 февраля 2010

В общем, я бы использовал первый подход, так как он проще. Однако в некоторых случаях требуется второе.

Обратите внимание, что клонирование объектов в качестве примера может быть не оптимальным. Особенность клонирования заключается в том, что возвращаемый объект должен быть того же типа, что и клонируемый объект, даже если переопределенный метод имеет базовый тип.

Итак, предположим, что у вас есть класс B, который наследуется от A, и вы делаете это:

A a = new B();
B b = (B)a.Clone();

Используя первый подход, клонированный объект должен быть создан A, но тип должен быть типа B. Как правило, вы будете использовать object.MemberwiseClone() для достижения этого, но это мешает вам заменить поля только для чтения, поскольку может быть сделано только в конструкторе (например, клонирование List, который не должен быть общим для клона и исходного объекта).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...