隔离仅由特定类实例化的类:最佳实践

本文关键字:最佳 实例化 隔离 | 更新日期: 2023-09-27 18:25:53

好的,我使用的是两层类:某种"管理器"类,任务是获取一个对象,链接所有附加到它的项,并将其返回给调用者,以及一个"数据"类,其任务是根据特定请求调用数据库。

下面是我工作的一个例子。这是一个"经理"类:

public class CardManager
{
    private static readonly CardData mCardDAL = new CardData();
    public List<CardDisplay> ListCardsToShow(int _pageNumber, string _queryString, string _rarity, string _type, string _color, out int _totalCount)
    {
        List<CardDisplay> listToReturn = mCardDAL.ListCardsToShow(_pageNumber, _queryString, _rarity, _type, _color, out _totalCount);
        LinkListCardDisplayData(listToReturn);
        return listToReturn;
    }

    /// <summary>
    /// Method links the card set with each cards of the list.
    /// </summary>
    /// <param name="_listToReturn"></param>
    private static void LinkListCardDisplayData(IEnumerable<CardDisplay> _listToReturn)
    {
        try
        {
            foreach (CardDisplay item in _listToReturn)
            {
                LinkCardDisplayData(item);
            }
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }
    private static void LinkCardDisplayData(CardDisplay _item)
    {
        _item.mMasterCardID = _item.mMasterCard.mCardID;
        ImagesManager.GetCardImages(_item);
        if (_item.mChildCard != null)
        {
            _item.mChildCardID = _item.mChildCard.mCardID;
        }
    }
}

这里有一个"数据"类,即本次事件中的CardData类:

public class CardData
{
    internal List<CardDisplay> ListCardsToShow(int _pageNumber, string _queryString, string _rarity, string _type, string _color, out int _totalCount)
    {
        using (DatabaseEntity db = new DatabaseEntity())
        {
            db.Database.Connection.Open();
            List<CARD> cardData;
            List<CardInfo> listCards;
            if (!String.IsNullOrWhiteSpace(_queryString))
            {
                var predicate = GetCardPredicate(_queryString);
                if (_rarity != "All")
                {
                    predicate = predicate.And(_item => _item.CARD_RARTY == _rarity);
                }
                if (_color != "All")
                {
                    predicate = predicate.And(
                            _item => _item.CARD_MANA_COST.Contains(_color) || _item.CARD_COLOR.Contains(_color));
                }
                if (_type != "All")
                {
                    predicate = predicate.And(_item => _item.CARD_TYPE.Contains(_type));
                }
                var cardQry = from c in db.CARD.AsExpandable().Where(predicate)
                              select c;
                _totalCount = cardQry.Count();
                int pageCount = _pageNumber - 1;
                cardData = cardQry.OrderBy(_x => _x.CARD_IDE).Skip(pageCount * 20).Take(20).ToList();
                for (int i = 0; i < cardData.Count; i++)
                {
                    CARD card = cardData[i];
                    if (cardData.Any(_item => _item.CARD_MASTER_IDE == card.CARD_IDE))
                    {
                        cardData.Remove(card);
                    }
                }
                listCards = DataConverter.ListCardDATAToListCardInfo(cardData);
            }
            else
            {
                // If we are here then the user browsed to get the 300 latest entries available.
                Expression<Func<CARD, bool>> cardPredicate = PredicateBuilder.True<CARD>();
                if (_rarity != "All")
                {
                    cardPredicate = cardPredicate.And(_item => _item.CARD_RARTY == _rarity);
                }
                if (_type != "All")
                {
                    cardPredicate = cardPredicate.And(_item => _item.CARD_TYPE.Contains(_type));
                }
                if (_color != "All")
                {
                    cardPredicate =
                        cardPredicate.And(
                            _item => _item.CARD_MANA_COST.Contains(_color) || _item.CARD_COLOR.Contains(_color));
                }
                var cardQry = (from c in db.CARD.AsExpandable().Where(_item => !_item.CARD_NAME.Contains("(Foil)"))
                                select c).OrderByDescending(_x => _x.CARD_SET.CARD_SET_RELES_DATE).Take(300);
                cardQry = cardQry.Where(cardPredicate);
                _totalCount = cardQry.Count();
                int pageCount = _pageNumber - 1;
                cardData = cardQry.Skip(pageCount * 20).Take(20).ToList();
                for (int i = 0; i < cardData.Count; i++)
                {
                    CARD card = cardData[i];
                    if (cardData.Any(_item => _item.CARD_MASTER_IDE == card.CARD_IDE))
                    {
                        cardData.Remove(card);
                    }
                }
                listCards = DataConverter.ListCardDATAToListCardInfo(cardData);
            }
            List<CardDisplay> listToReturn = MakeListCardDisplay(listCards);
            return listToReturn;
        }
    }
}

我的问题不是"如何"编写代码(但我喜欢学习,可以随意发表建设性的评论),而是它的结构。例如,我希望CardData类由"管理器"类独占地实例化,这意味着我不能在控制器中创建public static readonly CardData mCardDAL = new CardData();对象。

有没有一种方法可以隔离我的类,使任何人都"被迫"通过管理器来获取对象?我的工作方式很好,我的代码好吗?

隔离仅由特定类实例化的类:最佳实践

可以通过使CardData成为Manager内部的嵌套类来拒绝外部访问,通过使其成为protected来阻止其构造函数,并创建一个private实例类:

public class Manager
{
    public class Manager
    {
        CardData c = new CardDataInternal();
    }
    private class CardDataInternal : CardData
    {
        public CardDataInternal()
        { }
    }
    public class CardData
    {
        protected CardData()
        { }
    }
}

您可以通过使manager类成为嵌套类,然后为Card类提供一个私有构造函数(允许嵌套类调用)来保持这两个对象的公共性,类似于:

public class Card
{
    private Card()
    {
    }
    public class Manager
    {
        public Card Create()
        {
            return new Card();
        }
    }
}

因此,您可以自由创建new Card.Manager(),但创建新Card的唯一方法是通过Create()方法。

使用接口的@Patrick方法的替代方案:

public interface ICardData { ... }
public class Manager
{
    public class Manager()
    {
        CardData c = new CardData();
    }
    private class CardData : ICardData
    {
        public CardData() { }
    }
}

通过在嵌套的私有类上使用接口而不是构造函数,您还可以使用mock实现进行单元测试。。。可能对CardData来说并不重要,但如果你在一个有有趣行为的类上这样做,那就非常重要了。

从提供的代码来看,您可能希望将CardData类放在CardManager类中,并将其设为私有类,以便只有CardManager才能访问它。

public class CardManager
{
    private class CardData { ... }
}