Вопрос о дизайне класса - PullRequest
4 голосов
/ 30 июля 2010

Допустим, я хочу создать класс HumanBody. И я хочу сохранить длину каждой конечности.

HumanBody.LeftArmLength = 14;
HumanBody.RightArmLength = 14.1;
HumanBody.LeftLegLength = 32;
HumanBody.RightLegLength = 31.9;

Это все хорошо, но кажется, что было бы лучше сделать что-то вроде:

HumanBody.Arm.Left.Length = 14;
HumanBody.Arm.Right.Length = 14.1;
HumanBody.Leg.Left.Length = 32;
Humanbody.Leg.Right.Length = 31.9;

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

Редактировать: этот пример довольно прост, но если хранится 100 различных фрагментов данных, этот способ выглядит гораздо лучше.

Ответы [ 10 ]

10 голосов
/ 30 июля 2010

Левая и правая руки являются экземплярами руки, так что, возможно:

HumanBody.LeftArm.Length = 14; 
HumanBody.RightArm.Length = 14.1; 
HumanBody.LeftLeg.Length = 32; 
Humanbody.RightLeg.Length = 31.9; 

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

[Примечание: как было отмечено в комментариях, в идеале их следует устанавливать в конструкторе, а не использовать свойства. Это общий принцип: конструируйте объекты в согласованном, пригодном для использования состоянии.]

8 голосов
/ 30 июля 2010

Это зависит от ситуации real .Здесь, похоже, нет очень особого смысла группировать две руки вместе и две ноги вместе ... концепция "пары рук", как правило, не очень полезна.

С другой стороны, если вы говорили о группировке «DeliveryAddress1», «DeliveryAddress2», «DeliveryTown», «DeliveryZipCode», «BillingAddress1», «BillingAddress2», «BillingTown» и «BillingZipCode» в два экземплярадля класса Address это другой вопрос.

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

3 голосов
/ 30 июля 2010

Возможно, у вас есть

HumanBody.LeftArm.Length

где ваши уроки будут примерно такими:

public class HumanBody
{
    public Arm LeftArm { get; private set; }
}

public class Arm
{
    public double Length { get; private set; }
}

Но я не вижу преимущества в вашей руке

public class HumanBody
{
    public Arms Arm { get; private set; }
}

public class Arms
{
    public Arm Left { get; private set; }
    public Arm Right { get; private set; }
}

public class Arm
{
    public double Length { get; private set; }
}

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

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

HumanBody.LeftArm.Forefinger.Length = 3.2

Подходит для композиции поверх гораздо менее разборчивого (и менее функционального)

HumanBody.LeftArmsForefingerLength = 3.2
2 голосов
/ 30 июля 2010

Какой у вас основной объект? Мне кажется, что это HumanBody, и что длины придатков являются просто атрибутами этого.

Обязательно имейте коллекцию Arms, если вы думаете, что вы найдете много людей с более чем двумя руками: -)

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

1 голос
/ 30 июля 2010

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

Кстати, я знаю, что ваши примеры довольно надуманные, но у второго есть смысл, что у HumanBody есть только одна рука и только одна нога с левой и правой стороны. В этом конкретном случае может быть лучше иметь свойства RightArm, LeftArm, RightLeg и LeftLeg, которые возвращают их соответствующие экземпляры класса. Конечно, это возвращает вас к первому примеру, если единственный атрибут, который вы хотите связать с конечностями, это их длина ... просто пища для размышлений.

0 голосов
/ 30 июля 2010

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

public abstract class BodyPart
{
}

Вы можете добавить определенные типы частей тела:

public abstract class Limb : BodyPart
{
   public float Length { get; set; }
   public abstract void Move();
}

Если вам нужно, создайте подклассы для рук, ног и т. д.

public class Arm : Limb
{
   public override void Move()
   {
      Debug.WriteLine("Arm is moving");
   }
}

Наконец, вы можете разделить свое тело на подклассы для человека:

public class HumanBody : Body
{
   //properties and methods specific to the human body
}

Собрав все это вместе, вы можете динамически создать надежный HumanBody:

HumanBody body = new HumanBody();
Arm leftArm = new Arm(){ Length=24F };
Arm rightArm = new Arm(){ Length=24F };
//create other parts
body.BodyParts.Add(new KeyValuePair<string, BodyPart>("LeftArm",leftArm));
body.BodyParts.Add(new KeyValuePair<string, BodyPart>("RightArm",rightArm));
//add other parts

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

Arm leftArm = body.BodyParts["LeftArm"] as Arm;
leftArm.Move();

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

Это мой 2c.

0 голосов
/ 30 июля 2010

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

Конечно, мы можем применять эти принципы и к ногам.

Что касается ограничения длины конструктору, учитываются ли протезы?

0 голосов
/ 30 июля 2010

Просто чтобы поставить два моих цента в голосование, мы соглашаемся с естественной бисимметрией человека:

public class Digit { public double Length { get; set; } }

public class HumanSide
{
    public Digit Arm { get; set; }
    public Digit Leg { get; set; }
    public Digit MiddleFinger { get; set; }
    public Digit Foot { get; set; }
    public Digit Nose { get; set; }
}

public class Human
{
    public HumanSide Right { get; set; }
    public HumanSide Left { get; set; }
}
0 голосов
/ 30 июля 2010

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

class Bilateral
{
    Arm arm;
    Leg leg;
    Eye eye;
    Ear ear;
    ...
}

class HumanBody
{
    Bilateral left, right;
    Liver liver;
}

Так что вы бы сделали что-то вроде HumanBody.liver.weight = 3.14; или HumanBody.left.arm.length = 12.34;.

0 голосов
/ 30 июля 2010

Настройка иерархии объектов зависит от ваших потребностей. Иногда это имеет смысл, иногда нет.

Например:

//This make sense
public class Person {

    public String FirstName { get; set; }
    public String LastName { get; set; }
    public String FullName
    {
        get { return String.Format("{0} {1}", FirstName, LastName); }
    }

    public Address Address { get; set; }
    public PhoneNumber[] ContactNumbers { get; set; }
}

//This does not make as much sense, but it's still possible.
public class Person {
    public Name Name { get; set; }
    public Address Address { get; set; }
    public PhoneNumber[] ContactNumbers { get; set; }
}

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

...