C # О расширении большого класса в пользу читабельности - PullRequest
1 голос
/ 25 мая 2011

У меня есть большой абстрактный класс, который обрабатывает оружие в моей игре. Боевые циклы через список основных функций:

OnBeforeSwing
OnSwing
OnHit || OnMiss

Я имею в виду, что все расчеты, связанные с боевым уроном, переносятся в другую папку, которая обрабатывает именно это. Боевые расчеты, связанные с уроном.

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

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

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

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

Опять же, возможно, я мог бы сделать это, вызвав что-то вроде CombatSystem.HandleWeaponHit из OnHit в классе оружия, и не использовать методы расширения. Это может быть более подходящим.

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

Ответы [ 3 ]

2 голосов
/ 25 мая 2011

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

В похожих системах, которые я разработал, были действия и эффекты. Это были базовые классы. Каждое конкретное действие (атака пулеметом, определенное заклинание и т. Д.) Было классом, производным от действия. Действия имели список одного или нескольких конкретных эффектов, которые можно применить к целям. Это было достигнуто с помощью Dependency Injection.

Боевой двигатель не делал всю математику сам. По сути, он попросил цель рассчитать свой рейтинг защиты, затем перебрал все активные действия и попросил их определить, применяется ли какой-либо из его эффектов к цели. Если они применили, он попросил Действие применить соответствующие Эффекты к Цели.

Таким образом, боевой двигатель мал, а каждый эффект очень мал и прост в обслуживании.

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

1 голос
/ 25 мая 2011

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

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

0 голосов
/ 27 мая 2011

ИМХО Майк Хофер приводит потенциальных клиентов.

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

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

ВВ общем, каждый метод в вашем проекте всегда должен состоять из нескольких строк кода, не более.Для каждой части расчета представьте, какие классы применимы к этим вычислениям.Затем сделайте это вычисление методом общего базового класса из них.

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

Затем вы можете создать метод элементарного расширения, как в этом поддельном примере:

public interface IExploding { int ExplosionRadius { get; } }

public class Grenade : IExploding { public int ExplosionRadius { get { return 30; } } ... }

public class StinkBomb : IExploding { public int ExplosionRadius { get { return 10; } } ... }

public static class Extensions
{
    public static int Damages(this IExploding explosingObject)
    {
        return explosingObject.ExplosionRadius*100;
    }
}

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

Надеюсь, это поможет вам!

...