这是一个糟糕的oop设计吗?

时间:2011-04-08 14:49:26

标签: c# .net oop design-patterns class-design

我有一个名为鸡的课,在鸡里我有一些方法, 所以在我实例化并调用Chicken方法的另一个类中,我可能会这样做:

Chicken chicken = new Chicken("Name","Description")


public void UpdateChicken(Chicken chicken)
{ 
   chicken.Update(chicken);
}

以上是否存在问题,或者是否存在问题,如果是这样,最好还有另一个类,例如ChickenCalculations,并执行以下操作:

public void UpdateChick(Chicken chicken)
{
    ChickenCalculations.Update(chicken);
}

这是一个实现:

Chicken chicken = new Chicken("Bob","Coolest Chicken", 4, 123, 5, 388, true, false, true);

Chicken anotherChicken = new Chicken()
anotherChicken.Update(chicken);
chicken.Update(chicken)

这是一个更实用的例子,而不是使用鸡:

public class AirlineBooking
{
    int BookingId {get;set;}
    string Name {get;set;}
    string Description {get;set;}
    decimal Price {get;set;}
    decimal Tax {get;set;}
    string seat {get;set;}
    bool IsActive {get;set;}
    bool IsCanceld {get;set;}


    public AirlineBooking(string name, string description, decimal price, 
                          decimal tax, string seat, bool isActive, bool isCanceled)
    {
        Name = name;
        Description = description;
        Price = price;
        Tax = tax;
        Seat = seat;
        IsActive = isActive;
        IsCanceled = isCanceled;
    }

    public Update(AirlineBooking airlineBooking, int id)
    {
          //Call stored proc here to update booking by id
    }

    public class BookingSystem
    {
       //Create new booking
       AirlineBooking booking = new AirlineBooking("ticket-1",
                                                   "desc",150.2,22.0,
                                                   "22A",true, false);

       //Change properties and update.
       booking.Name ="ticket-2";
       booking.Description = "desc2";
       booking.Price = 200.52;
       booking.Tax = 38.50;

       public void UpdateBooking(AirlineBooking booking, int id)
       {
            /* This is the meat of the question, should the passed in booking to
               update itself or should I have a Service Class , such as
               AirlineBookingOperations with an update method. */
            booking.Update(booking,id);
       }
    }
}

7 个答案:

答案 0 :(得分:27)

为什么UpdateChicken函数不是Chicken类的成员?

这样,您就不必传入Chicken对象的实例,而只需在现有实例上调用Update方法:

Chicken chicken = new Chicken("Name", "Description");
chicken.Update();

通常最好封装在该类的内部上运行的所有方法,而不是将它们拆分为单独的“helper”类。让他们自己管理自己!

答案 1 :(得分:9)

面向对象编程的整个想法是将对象视为能够对自己采取行动。

所以你应该使用chicken.Update()来更新鸡。

答案 2 :(得分:8)

我将以您的AirlineBooking课程为例,因为很多人似乎对Chicken示例感到困惑。

一些介绍:

Single responsibility principle指出一个对象应该具有单一责任,并且它应该只涉及与该责任相符的事物。例如,TaxCalculator负责计算税金,而不是例如转换货币 - 这是CurrencyConverter的工作。

这通常是非常好的主意,因为这意味着您的应用程序被组织成代码块,每个代码都有一个单一的责任,使其更容易理解,更安全。另一种方法是,一个类或模块应该只有一个改变的理由,例如“我们计算税收的方式已经改变”,或“我们转换货币的方式已经改变”。


您需要问自己的问题是:

  • AirlineBooking的责任是什么?
  • 更新航空公司预订部分责任吗?

例如,在这种情况下,我会说AirlineBooking的责任是“封装航空公司预订”,更新航空公司预订实际上是预订系统的责任,而不是AirlineBooking

另外,另一种思考方式是,如果我将Update方法放在AirlineBooking上,这意味着:

  • 如果预订系统更改为使用Web服务而不是存储过程,则AirlineBooking类需要更改。
  • 如果航空公司预订的封装发生变化(可能暂停预订,或者现在记录了航空公司的名称),那么AirlineBooking需要更改。

即。 AirlineBooking现在有许多不同的原因需要改变,所以它也不应该对“更新”负责


简而言之,我可能会这样做:

public class AirlineBooking
{
    public int BookingId {get;set;}
    /* Other properties */
}

public class BookingSystem
{
    public void UpdateBooking(AirlineBooking booking, int id)
    {
        // Call your SP here.
    }
}

您应该问自己这些问题的原因是因为它取决于您的应用程序中AirlineBooking的用途。

例如,如果AirlineBooking“知道”(即有参考)预订系统,那么您可以添加“帮助”方法,如下所示:

public class AirlineBooking
{
    public void Update(int id)
    {
        this.bookingSystem.UpdateBooking(this, id);
    }
}

答案 3 :(得分:3)

为什么不给你的鸡类一个方法“更新(一些参数......)”?然后,你可以通过

实现一只鸡
Chicken chicken = new Chicken("Name", "descr");

并按:

更新
chicken.Update(myparameters..);

修改

public class Chicken
{
  public Chicken(string name, string description)
  {
     this.Name = name;
     this.Description = description;
  }

  public string Name { get; set; }
  public string Description { get; set; }

  // Fill in all the other properties!

  public int EggsDroppedInLife { get; set; }
}

现在你可以通过以下方式使用你的鸡类:

Chicken chicken = new Chicken("Harry", "Nice chick");
chicken.NumberOfEggs = 123;
chicken.Description = "Oh no, it's actually not nice.";
// ... change all the properties as you want

答案 4 :(得分:3)

对象应该封装功能。应传递功能以使封装对象具有灵活性。

所以,如果你要保存鸡,你应该传入存储库功能。如果你有鸡计算,并且它们很容易改变,那么它也应该被传入。

class Chicken
{
   IChickenCalculations ChickenCalculations;
   IChickenRepository ChickenRepository;
   Chicken(IChickenCalculations chickenCalculations, IChickenRepository chickenRepository)
   {
       ChickenCalculations = chickenCalculations;
       ChickenRepository = chickenRepository ;
   }

   Calculate()
   {
       ChickenCalculations.Calculate(this);
   }
   Update()
   {
       ChickenRepository.Update(this);
   }
}

请注意,在这个例子中,鸡只能够对自身进行计算并坚持自己,而不知道如何进行计算或持久化(毕竟,它只是一只鸡)。

答案 5 :(得分:1)

虽然我发现没有Chicken,但您的真实对象上可能有Update方法,对吧?

我认为你应该尝试在语言方面介绍除“更新”之外的其他内容。没有办法真正理解更新的作用。它只是更新鸡肉中的“数据”吗?在那种情况下,有什么数据?而且,你是否应该像这样更新鸡的实例?

我宁愿看到像

这样的东西
chicken.CanFly = false;
if(chicken.CanFly)  // inherited from Bird :)
    ckicken.FlyTo(point);
else
    chicken.WalkTo(point);

这是OOP中非常有趣的练习:http://milano-xpug.pbworks.com/f/10080616-extreme-oop.pdf

答案 6 :(得分:0)

对于多线程环境,像ChickenCalculations这样的单独类更适合。除了chicken.Update()之外,当你需要执行一些其他步骤时,你可以使用ChickenCalculations类来完成。因此,如果在Chicken上实例化和调用方法的多个类不必担心ChickenCalculations类正在处理的事情。