Общая практика бизнес-объектов (и ошибки исключений - избыточность) - PullRequest
2 голосов
/ 25 октября 2008

Недавно, Я сделал сообщение о разработчиках, с которыми я работаю, неправильно использующих блоки try catch , и к сожалению использующих блоки try ... catch в критических ситуациях и игнорирующих ошибку исключения все вместе. вызывает у меня сильную боль в сердце. Вот пример одного из нескольких тысяч разделов кода, в котором они это делали (какой-то код, который не имеет особого значения:

public void AddLocations(BOLocation objBllLocations)
{
    try
    {
        dbManager.Open();
        if (objBllLocations.StateID != 0)
        {
             // about 20 Paramters added to dbManager here
        }
        else
        {
           // about 19 Paramters added here
        }
        dbManager.ExecuteNonQuery(CommandType.StoredProcedure, "ULOCATIONS.AddLocations");
    }
    catch (Exception ex)
    {
    }
    finally
    {
        dbManager.Dispose();
    }
}

Это абсолютно отвратительно, на мой взгляд, и не уведомляет пользователя в случае возникновения потенциальной проблемы. Я знаю, что многие люди говорят, что ООП - это зло, и что добавление нескольких слоев увеличивает количество строк кода и усложняет программу, что приводит к возможным проблемам с поддержкой кода. Лично я во многом программировал, применяя в этой области почти тот же подход. Ниже я перечислил базовую структуру того, как я обычно кодирую в такой ситуации, и я делал это на многих языках в моей карьере, но этот конкретный код написан на C #. Но приведенный ниже код является хорошей базовой идеей о том, как я использую Объекты, мне кажется, это работает, но, поскольку это хороший источник некоторых довольно интеллектуальных программных мин, я хотел бы знать, стоит ли мне переоценивать это Техника, которую я использовал в течение многих лет. Главным образом потому, что в ближайшие несколько недель я собираюсь погрузиться в не очень хороший код от сторонних разработчиков и модифицировать огромные участки кода. Я хотел бы сделать это как можно лучше. извините за длинный код ссылки.

// *******************************************************************************************
/// <summary>
/// Summary description for BaseBusinessObject
/// </summary>
/// <remarks>
/// Base Class allowing me to do basic function on a Busines Object
/// </remarks>
public class BaseBusinessObject : Object, System.Runtime.Serialization.ISerializable
{
    public enum DBCode
    {   DBUnknownError,
        DBNotSaved,
        DBOK
    }

    // private fields, public properties
    public int m_id = -1;
    public int ID { get { return m_id; } set { m_id = value; } }
    private int m_errorCode = 0;
    public int ErrorCode { get { return m_errorCode; } set { m_errorCode = value; } }
    private string m_errorMsg = "";
    public string ErrorMessage { get { return m_errorMsg; } set { m_errorMsg = value; } }
    private Exception m_LastException = null;
    public Exception LastException { get { return m_LastException; } set { m_LastException = value;} }

    //Constructors
    public BaseBusinessObject()
    {
        Initialize();
    }
    public BaseBusinessObject(int iID)
    {
        Initialize();
        FillByID(iID);
    }
    // methods
    protected void Initialize()
    {
        Clear();
        Object_OnInit();
        // Other Initializable code here
    }
    public void ClearErrors()
    {
        m_errorCode  = 0; m_errorMsg = ""; m_LastException = null;
    }

    void System.Runtime.Serialization.ISerializable.GetObjectData(
         System.Runtime.Serialization.SerializationInfo info, 
        System.Runtime.Serialization.StreamingContext context)
    {
      //Serialization code for Object must be implemented here
    }
    // overrideable methods
    protected virtual void Object_OnInit()     
    {
        // User can override to add additional initialization stuff. 
    }
    public virtual BaseBusinessObject FillByID(int iID)
    {
        throw new NotImplementedException("method FillByID Must be implemented");
    }
    public virtual void Clear()
    {
        throw new NotImplementedException("method Clear Must be implemented");
    }
    public virtual DBCode Save()
    {
        throw new NotImplementedException("method Save Must be implemented");
    }
}
// *******************************************************************************************
/// <summary>
/// Example Class that might be based off of a Base Business Object
/// </summary>
/// <remarks>
/// Class for holding all the information about a Customer
/// </remarks>
public class BLLCustomer : BaseBusinessObject
{
    // ***************************************
    // put field members here other than the ID
    private string m_name = "";
    public string Name { get { return m_name; } set { m_name = value; } }
    public override void Clear()
    {
        m_id = -1;
        m_name = "";
    }
    public override BaseBusinessObject FillByID(int iID)
    {
        Clear();
        try
        {
            // usually accessing a DataLayerObject, 
            //to select a database record
        }
        catch (Exception Ex)
        {
            Clear();
            LastException = Ex;
            // I can have many different exception, this is usually an enum
            ErrorCode = 3;
            ErrorMessage = "Customer couldn't be loaded";
        }
        return this;
    }
    public override DBCode Save()
    {
        DBCode ret = DBCode.DBUnknownError;
        try
        {
            // usually accessing a DataLayerObject, 
            //to save a database record
            ret = DBCode.DBOK;
        }
        catch (Exception Ex)
        {
            LastException = Ex;
            // I can have many different exception, this is usually an enum
            // i do not usually use just a General Exeption
            ErrorCode = 3;
            ErrorMessage = "some really weird error happened, customer not saved";
            ret = DBCode.DBNotSaved;
        }
        return ret;
    }
}
// *******************************************************************************************
// Example of how it's used on an asp page.. 
    protected void Page_Load(object sender, EventArgs e)
    {
        // Simplifying this a bit, normally, I'd use something like, 
        // using some sort of static "factory" method
        // BaseObject.NewBusinessObject(typeof(BLLCustomer)).FillByID(34);
        BLLCustomer cust = ((BLLCustomer)new BLLCustomer()).FillByID(34);
        if (cust.ErrorCode != 0)
        {
            // There was an error.. Error message is in 
            //cust.ErrorMessage
            // some sort of internal error code is in
            //cust.ErrorCode

            // Give the users some sort of message through and asp:Label.. 
            // probably based off of cust.ErrorMessage
            //log can be handled in the data, business layer... or whatever
            lab.ErrorText = cust.ErrorMessage;
        }
        else
        {
            // continue using the object, to fill in text boxes, 
            // literals or whatever. 
            this.labID = cust.ID.toString();
            this.labCompName = cust.Name;
        }
    }

Суть в том, что мой вопрос таков: не слишком ли сложно усложнить работу со множественными слоями и унаследованными классами, или моя старая иллюстрированная концепция все еще работает хорошо и стабильно? Есть ли лучший способ сделать это сейчас? Должен ли я просто делать прямые вызовы SQL из кода страницы asp.net за страницами, как предлагал коллега-разработчик (хотя это последнее решение заставляет меня чувствовать себя странно), вместо того, чтобы проходить через бизнес-объект и уровень данных (уровень данных не показано, но в основном содержит все сохраненные вызовы процедур). Да, еще один разработчик спросил меня, почему я делаю многоуровневую работу, когда вы можете просто напечатать то, что вам нужно, прямо в коде * .aspx.cs за страницей, и тогда я получу удовольствие от более чем 1 тыс. Строк кода позади. Какой совет здесь?

Ответы [ 6 ]

1 голос
/ 25 октября 2008

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

1 голос
/ 25 октября 2008

Рассматривали ли вы использовать ORM как NHibernate? Нет смысла заново изобретать колесо.

Для меня это кодовый запах:

BLLCustomer cust = ((BLLCustomer)new BLLCustomer()).FillByID(34);

Слишком много скобок!

Я обнаружил, что использование шаблона активной записи на языке, подобном C #, всегда заканчивается слезами, потому что это сложно (er) для модульного тестирования.

0 голосов
/ 25 октября 2008

Ваш способ обработки ошибок кажется очень устаревшим. Просто создайте новое исключение и наследуйте от исключения таким образом, чтобы у вас был хотя бы колл-стэк. Тогда вы должны войти с чем-то вроде nlog или log4net. И это 2008 год, так что используйте дженерики. Таким образом, вам придется намного меньше кастовать.

И используйте ORM, как кто-то сказал ранее. Не пытайтесь изобретать велосипед.

0 голосов
/ 25 октября 2008

Можно ли использовать абстрактный базовый класс вместо конкретного класса? это приведет к реализации ваших методов во время разработки, а не во время выполнения исключений.

Лучший комментарий, с которым здесь следует согласиться, - это танцы, где вы должны обрабатывать только те исключения, которые вы можете восстановить в этот момент. Поймать других и отбросить - лучший метод (хотя я думаю, что это редко делается). Также убедитесь, что они зарегистрированы ....:)

0 голосов
/ 25 октября 2008

Мое эмпирическое правило заключается в том, чтобы отлавливать только те ошибки, которые я могу обработать, или давать пользователю что-то полезное, чтобы, если они делают то же, что и они, они делали снова, это, вероятно, сработало бы для них. Я ловлю исключения из базы данных; но только добавить еще немного информации об ошибке об используемых данных; тогда я перебрасываю это. В общем, лучший способ обработки ошибок - это вообще их не отлавливать, кроме как на вершине стека пользовательского интерфейса. Наличие одной страницы для обработки ошибок и использование global.asax для маршрутизации на нее обрабатывает практически все ситуации. Использование кодов состояния определенно вышло из моды вместе. Это остаток СОМ.

0 голосов
/ 25 октября 2008

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

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