Является ли плохой практикой для объекта перехватывать свое собственное исключение и сохранять сообщение в свойстве? - PullRequest
3 голосов
/ 17 февраля 2009

Допустим, у меня есть список объектов одного типа. Я хочу перебрать этот список объектов и выполнить «опасный» метод для каждого из них. Если в этом методе возникает исключение, является ли плохой метод, чтобы сам метод перехватывал исключение и устанавливал свойство ошибки для объекта?

Вот краткий пример, когда метод Start () класса Car перехватывает свое собственное исключение и сохраняет его в свойстве «Problem»:

Sub Main()

    // Build cars.
    Dim ford As New Car("ford", "1988")
    Dim chevy As New Car("chevy", "1992")
    Dim honda As New Car("honda", "2002")

    // Create a list of cars.
    Dim carList As New List(Of Car)
    carList.Add(ford)
    carList.Add(chevy)
    carList.Add(honda)

    // Start all of the cars.
    For Each c As Car In carList
        // I don't want to stop processing if an exception occurs.
        // And I don't care to report any errors now.
        c.Start()
    Next

    // other stuff...

    // Now report errors.
    Dim cr As New CarReport
    cr.ReportProblems(carList)

End Sub

Класс, сообщающий о любых проблемах:

Public Class CarReport

    Public Sub ReportProblems(ByVal c As List(Of Car))
        // Report any problems.
        For Each c In carList
            If Not String.IsNullOrEmpty(c.Problem) Then
                Console.WriteLine(c.Problem)
            End If
        Next
    End Sub

End Class

Простой класс автомобиля:

Public Class Car

    Private _make As String
    Private _year As String
    Private _problem As String = String.Empty

    Public Sub New(ByVal make As String, ByVal year As String)
        _make = make
        _year = year
    End Sub

    Public Sub Start()
        Try
            // Do something that could throw an exception.
        Catch ex As Exception
            _problem = "Car would not start."
        End Try
    End Sub

    Public ReadOnly Property Problem() As String
        Get
            Return _problem
        End Get
    End Property

End Class

РЕДАКТИРОВАТЬ: Что если вызывающий (то есть, Sub Main() выше) поймал исключение и затем установить свойство Problem для объекта Car? Это кажется чище. Что мне действительно нужно сделать, так это сохранить текст ошибки с конкретным объектом Car, чтобы его можно было передавать в другую систему для создания отчетов.

Примечание: Автомобильный сценарий является тривиальным примером для иллюстрации сценария, я не особо задумывался над дизайном класса.

Ответы [ 7 ]

4 голосов
/ 17 февраля 2009

Это не плохая практика, это шаблон проектирования, который используется в ADO.Net.

Каждая строка в наборе данных имеет свойство с именем HasErrors . Это полезно в различных сценариях проверки.

Вот пример Бет Масси: " Отображение сообщений проверки данных в WPF "

РЕДАКТИРОВАТЬ : @casperOne: Спасибо за ваш комментарий. Да, поведение HasError нарушает идеал инкапсуляции - когда у объекта возникает ошибка, некоторое внешнее действие должно собирать / использовать эту информацию, а затем уничтожать / сбрасывать объект.

Но это хороший компромисс простоты. «Правильная» реализация, включающая обертки, будет сложной и дорогой, поэтому нецелесообразной.

Это не только обращение к власти, это призыв к "проклятому делу!" ;)

4 голосов
/ 17 февраля 2009

Я бы сказал абсолютно, потому что это только условие для одного звонка. Что произойдет, если вы сделаете другой вызов для объекта, и он также выдаст исключение? Значение свойства переопределяется, и тогда вы не уверены, в каком состоянии находится объект.

Вам лучше собирать информацию об исключениях в блоке try / catch, а затем работать с ним отдельно. Вообще говоря, если вы не разработали метод объекта, чтобы не изменять состояние объекта, я бы рассмотрел исключение, возникающее при вызове метода / свойства объекта, чтобы сделать объект непригодным для использования, как состояние объекта является неопределенным в этой точке.

3 голосов
/ 17 февраля 2009

Я так думаю по ряду причин:

(1) Это нарушает принцип Fail Fast , задерживая сообщение о проблеме.

(2) Это не очень модульная конструкция. Классы car и carReport слишком тесно связаны.

(3) От вызывающей стороны требуется наличие специальных знаний о реализации main (), чтобы уметь обращаться с ошибками, что повышает вероятность того, что клиентский код будет игнорировать условие ошибки либо из-за незнания ошибки Механизм погрузки или лень.

(4) Если у вас есть ситуация, в которой может произойти несколько ошибок, это имеет определенный запах, что, возможно, операцию следует разбить на более мелкие единицы работы.

(5) Как уже упоминалось в другом ответе. Методы car.problem и car.start слишком тесно связаны. Например, что произойдет, если вы дважды вызовете car.start, и он потерпит неудачу только один раз? Вы сохраняете первую проблему? Как вызывающий абонент знает, как интерпретировать свойство car.problem в этой ситуации. В целом это выглядит как плохая инкапсуляция.

1 голос
/ 17 февраля 2009
var errors = new string[carList.Length];
for(var i = 0; i < carList.Length; i += 1) {
    try {
        carList[i].Start();
    } catch(Exception ex) {
        errors[i] = ex.Message;
    }
}
1 голос
/ 17 февраля 2009

Я думаю, что необходимо проводить различие между «проверкой данных» и «ошибками программы».
В сценарии проверки данных:

Если проверка выполняется полностью или частично посредством исключений, перехват исключения и его применение к свойству либо с помощью ADO.NET HasErrors, либо с IDataErrorInfo, либо с любыми другими нужными средствами - это хорошо. Есть много аргументов по поводу того, является ли использование исключений для проверки правильностью практики программирования или нет, и я видел хороший код по обе стороны границы.

Однако, если возникает исключение, которое не связано с вашей проверкой (NullReferenceException или MemoryException), так что ваш объект больше не может работать правильно, поскольку его состояние не определено, то исключение необходимо вызвать по указанным причинам. в ответе JohnFx.

0 голосов
/ 15 февраля 2012

Есть сценарии, в которых можно попробовать выполнить несколько операций и посмотреть, какие из них успешны. В некоторых сценариях может потребоваться попытаться выполнить более поздние операции, даже если предыдущие не удаются. Мне не нравится ваш конкретный шаблон, хотя. Я хотел бы предположить, что было бы лучше, если бы Start включал перегрузку, которая принимает что-то вроде Func<Exception,someEnumeratedType> (возможно, Action<Exception,somethingElse>). Если возникнет проблема, код создаст Exception и передаст его в Action; функция по умолчанию просто выдает переданное исключение, но функция также может регистрировать сбой и возвращать значение, указывающее, что все должно продолжаться или что они должны корректно завершиться.

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

0 голосов
/ 17 февраля 2009

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

Хотя этот образец используется в ADO.Net, как сказал «Том А», я думаю, что он используется в другом контексте. Как ясно заявить, что это контекст проверки. Никаких реальных ошибок / исключений не произошло, но это возможно! Там у вас есть возможность исправить данные, которые могут произойти сбой, в контексте, используемом здесь, ошибка уже произошла.

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