Как лучше всего разрабатывают масштабируемый класс?

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

class Apple

  float seedCount;
  ...
  ...about 25 variables and properties here.
  void Update() <-- a huge method that checks for each property and updates if so

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

class Apple

  Seed seed;

Много проблем из-за этого: Я постоянно должен проверять на каждый объект и функцию каждый кадр. Если семя не инициализируется, я ничего не должен вычислять для него. Если это, я имею к. Если бы я решил поместить больше чем 1 свойство/функцию в класс Семени, то я должен проверить каждые из тех также. Это просто становится все более сложным. Проблема, которую я имею, поэтому, что я нуждаюсь в детализированном управлении всеми функциями и не могу разделить их разумно на большие подклассы. Любая форма подкласса просто содержала бы набор свойств, которые должны быть проверены и обновлены, если требуется. Я не могу точно создать подклассы Apple из-за потребности в таком высоком детализированном управлении. Это было бы безумие для создания стольких же классов, сколько существуют комбинации свойств. Моя главная цель: Я хочу короткий код.

5
задан ChaosPandion 7 July 2010 в 15:13
поделиться

8 ответов

Было бы безумием создавать столько классов, сколько существует комбинаций свойств.

Похоже, вы ищете паттерн декоратора. Его цель - облегчить управление объектами, которые могут иметь множество различных комбинаций свойств без экспоненциально растущей иерархии. У вас просто есть один небольшой подкласс для каждого свойства или поведения (не обязательно одно свойство C#, просто что-то, что вы можете сгруппировать вместе), а затем вы можете компоновать их вместе во время выполнения.

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

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

5
ответ дан 14 December 2019 в 08:40
поделиться

вы можете использовать вложенный класс в классе Apple. http://msdn.microsoft.com/en-us/library/ms173120 (VS.80) .aspx

1
ответ дан 14 December 2019 в 08:40
поделиться

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

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

1
ответ дан 14 December 2019 в 08:40
поделиться

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

Что касается проблемы необязательных функций, возможно, вы можете использовать null objects вместо нулевых ссылок. Таким образом, вам не нужно проверять наличие null каждый раз, и код более согласован.

1
ответ дан 14 December 2019 в 08:40
поделиться

Моя главная цель: мне нужен короткий код.

Варианты:

  1. Переписать все функции как статические и создать класс для каждой из них.
  2. Переписать кодовую базу на Perl.
  3. Удалите все комментарии.
-4
ответ дан 14 December 2019 в 08:40
поделиться

Может быть, ваши методы не те, какими должны быть?

Если вы отделили класс Seed от класса Apple, почему бы вам не переместить методы, использующие информацию Seed, в класс Seed тоже?

Если этим методам нужна информация о других свойствах Apple, вы можете передать ее в качестве параметра.

Делая это, я думаю, вы можете исключить проверки инициализации ...

Это отличная книга о том, как решить эту проблему:

Рефакторинг

0
ответ дан 14 December 2019 в 08:40
поделиться

Я немного обдумывал этот вопрос и нашел альтернативное решение. Это может быть немного неортодоксальным и антиобъектно-ориентированным, но если вы не слабохарактерны, прочтите ...

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

  1. Яблоко
    а. Семя
    а1. GetSeedCount
    а2. ...
    б. Кожа
    b1. GetSkinColor
    Би 2. ...

Я использую объект словаря для хранения всех свойств яблок.

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

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

Использование методов расширения для определения средств доступа к свойствам позволяет разделить код для каждой логической категории свойств. Это позволяет организовать вещи в отдельные блоки связанной логики.

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

Apple.cs

namespace ConsoleApplication1
{
    using System.Collections.Generic;
    using System.Text;

    public class Apple
    {
        // Define the set of valid properties for all apple objects.
        private static HashSet<string> AllowedProperties = new HashSet<string>(
            new string [] {
                "Color",
                "SeedCount"
            });

        // The main store for all properties
        private Dictionary<string, string> Properties = new Dictionary<string, string>();

        // Indexer for accessing properties
        // Access via the indexer should be restricted to the extension methods!
        // Unfortunately can't enforce this by making it private because then extension methods wouldn't be able to use it as they are now.
        public string this[string prop]
        {
            get
            {
                if (!AllowedProperties.Contains(prop))
                {
                    // throw exception
                }

                if (Properties.ContainsKey(prop))
                {
                    return this.Properties[prop];
                }
                else
                {
                    // TODO throw 'property unitialized' exeception || lookup & return default value for this property || etc.

                    // this return is here just to make the sample runable
                    return "0"; 
                }
            }

            set
            {
                if (!AllowedProperties.Contains(prop))
                {
                    // TODO throw 'invalid property' exception

                    // these assignments are here just to make the sample runable
                    prop = "INVALID";
                    value = "0";
                }

                this.Properties[prop] = value.ToString();
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            foreach (var kv in this.Properties)
            {
                sb.AppendFormat("{0}={1}\n", kv.Key, kv.Value);
            }

            return sb.ToString();
        }
    }
}

AppleExtensions. cs

namespace AppleExtensionMethods
{
    using System;
    using ConsoleApplication1;

   // Accessors for Seed Properties
    public static class Seed
    {
        public static float GetSeedCount(this Apple apple)
        {
            return Convert.ToSingle(apple["SeedCount"]);
        }

        public static void SetSeedCount(this Apple apple, string count)
        {
            apple["SeedCount"] = count;
        }
    }

   // Accessors for Skin Properties
    public static class Skin
    {
        public static string GetSkinColor(this Apple apple)
        {
            return apple["Color"];
        }

        public static void SetSkinColor(this Apple apple, string color)
        {
            apple["Color"] = ValidSkinColorOrDefault(apple, color);
        }

        private static string ValidSkinColorOrDefault(this Apple apple, string color)
        {
            switch (color.ToLower())
            {
                case "red":
                    return color;

                case "green":
                    return color;

                default:
                    return "rotten brown";
            }
        }
    }
}

Вот тест-драйв:

Программа.cs

namespace ConsoleApplication1
{
    using System;
    using AppleExtensionMethods;

    class Program
    {
        static void Main(string[] args)
        {
            Apple apple = new Apple();

            apple.SetSkinColor("Red");
            apple.SetSeedCount("8");

            Console.WriteLine("My apple is {0} and has {1} seed(s)\r\n", apple.GetSkinColor(), apple.GetSeedCount());

            apple.SetSkinColor("green");
            apple.SetSeedCount("4");

            Console.WriteLine("Now my apple is {0} and has {1} seed(s)\r\n", apple.GetSkinColor(), apple.GetSeedCount());

            apple.SetSkinColor("blue");
            apple.SetSeedCount("0");

            Console.WriteLine("Now my apple is {0} and has {1} seed(s)\r\n", apple.GetSkinColor(), apple.GetSeedCount());

            apple.SetSkinColor("yellow");
            apple.SetSeedCount("15");

            Console.WriteLine(apple.ToString());

            // Unfortunatly there is nothing stopping users of the class from doing something like that shown below.
            // This would be bad because it bypasses any behavior that you have defined in the get/set functions defined
            // as extension methods.
            // One thing in your favor here is it is inconvenient for user of the class to find the valid property names as
            // they'd have to go look at the apple class. It's much easier (from a lazy programmer standpoint) to use the
            // extension methods as they show up in intellisense :) However, relying on lazy programming does not a contract make.
            // There would have to be an agreed upon contract at the user of the class level that states, 
            //  "I will never use the indexer and always use the extension methods!"
            apple["Color"] = "don't panic";
            apple["SeedCount"] = "on second thought...";

            Console.WriteLine(apple.ToString());
        }
    }
}

Обращение к вашему комментарию от 7/11 (дата, а не магазин):)
В предоставленном вами примере кода есть комментарий, который гласит:

«Как видите, я могу» я звоню Методы BasicBroodmother на "монстре"

Вы понимаете, что в этот момент можете сделать что-то вроде этого:

BasicBroodmother bm = monster as BasicBroodmother;
if (bm != null)
{
    bm.Eat();
}

В вашем коде не так много мяса (я понимаю, что это был всего лишь пример), но когда я смотрю на него У меня такое чувство, что ты сможешь улучшить дизайн. Моей непосредственной мыслью было создание абстрактного класса для матерей-маток, который содержал бы реализации по умолчанию любых атрибутов / действий, общих для всех матерей-маток. Тогда специализированные матки, такие как магическая мать выводка, будут содержать любые специализированные атрибуты / действия, специфичные для магической матки, но также унаследованы от абстрактного класса и, если необходимо, переопределят необходимые базовые атрибуты / действия.

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

[edit 7/13]
У меня сейчас нет времени вдаваться в подробности (нужен сон), но я собрал пример кода , демонстрирующий другой подход.

Код состоит из:

  • Broodfather.cs - абстрактный класс, заполненный всем, что является общим для различных «типов» Broodfather.
  • BasicBroodFather.cs - конкретный класс, наследуемый от Broodfather.
  • BroodfatherDecorator.cs - абстрактный класс, который будет унаследован всеми декораторами Broodfather.
  • Магический брат-отец.cs - этот класс украшает / обертывает Broodfather «магией»
  • BloodthirstyBroodfather.cs - этот класс украшает / обертывает Broodfather «кровожадностью»
  • program.cs - демонстрирует два примера: Первый начинается с простого Broodfather, который окутывается магией, затем кровожадностью. Второй начинается с основного выводка и обертывает его в другом порядке: кровожадность, затем магия.
1
ответ дан 14 December 2019 в 08:40
поделиться

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

Я полностью избавился от декораторов. Абстрактный класс Broodfather остается. У него есть два дополнительных свойства: IBroodfatherMagicAbility и IBroodfatherBloodthirstAbility. Эти два свойства позволят вам получить доступ к различным атрибутам, относящимся к этим способностям, однако ключом ко всему этому является то, что стратегия реализации способностей может меняться во время выполнения (см. Шаблон стратегии ).

Есть два класса, каждый из которых реализует «стратегию» как для Bloodthrist, так и для магии.

  • IBroodfatherBloodthirstAbility.cs - это интерфейс, который должны реализовывать все «стратегии кровожадности».
  • BroodfatherNonBloodThristy.cs - класс, реализующий атрибуты для некровожадных.
  • BroodfatherBloodThristy.cs - класс, реализующий атрибуты для кровожадности.

  • IBroodfatherMagicAbility.cs - это интерфейс, который должны реализовывать все «магические стратегии».

  • BroodfatherNonMagical.cs - класс, реализующий немагическую стратегию.
  • BroodfatherMagical.cs - класс, реализующий стратегию магии.

  • BasicBroodfather.cs - это похоже на предыдущий пример, за исключением того, что теперь, когда создается экземпляр, свойства magic и bloodthrist устанавливаются на новые экземпляры немагических и не кровожадных стратегических объектов.

  • Program.cs - это драйвер, который показывает классы и то, как различные стратегии могут быть заменены местами во время выполнения.

Думаю, вы найдете это более подходящим для того, как вы хотели, чтобы все работало.

2
ответ дан 14 December 2019 в 08:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: