简体   繁体   English

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

[英]Is this bad oop design?

I have class called Chicken and in Chicken I have some methods, so in another class where I instantiate and call methods on Chicken, I might do something like this: 我有一个名为Chicken的类,在Chicken中我有一些方法,所以在另一个我实例化并调用Chicken方法的类中,我可能会这样做:

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


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

Is the above fine or does it present problems, if so, is it better to have another class, such as ChickenCalculations and do something like: 以上是罚款还是存在问题,如果是这样,是否更好地拥有另一个类,例如ChickenCalculations并执行以下操作:

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

Here is an implementation: 这是一个实现:

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

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

Here is a more practical example instead of using a 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);
       }
    }
}

Why isn't the UpdateChicken function a member of the Chicken class? 为什么UpdateChicken函数不是Chicken类的成员?

That way, you wouldn't have to pass in an instance of a Chicken object, but rather just call the Update method on an existing instance: 这样,您就不必传入Chicken对象的实例,而只需在现有实例上调用Update方法:

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

It's generally best to encapsulate all the methods that operate on a particular class inside of that class, rather than splitting them up into a separate "helper" class. 通常最好封装对该类内部特定类进行操作的所有方法,而不是将它们拆分为单独的“辅助”类。 Let them chickens manage themselves! 让他们自己管理!

The whole idea of Object Oriented Programing is to think of objects as able to act upon themselves. 面向对象编程的整个想法是将对象视为能够对自己采取行动。

So you should just use chicken.Update() to update a chicken. 所以你应该只使用chicken.Update()来更新鸡。

I'm going to use your AirlineBooking class as an example because a lot of people seem to have confused themselves over the Chicken example. 我将以你的AirlineBooking课程为例,因为很多人似乎对Chicken例子感到困惑。

Some introduction: 一些介绍:

The Single responsibility principle states that an object should have a single responsibility and that it should only concern itself with things narow aligned with that responsibility. 单一职责原则规定,一个对象应该有一个责任,它应该只关心自己的事情narow与责任一致。 For example a TaxCalculator should only be responsible for calculating tax and not, for example, with converting currency - this is the job of the CurrencyConverter . 例如, TaxCalculator应该负责计算税收,而不是例如转换货币 - 这是CurrencyConverter的工作。

This is often a really good idea , as it means that your application is structured into chunks of code, each one with a single responsibility making it easier to understand and safer to change. 这通常是一个非常好的主意 ,因为它意味着您的应用程序被组织成代码块,每个代码都有一个单一的责任,使其更容易理解,更安全。 Another way of putting this is that a class or module should have one and only one reason to change, for example "The way we calculate tax has changed", or "The way we convert currency has changed". 另一种方法是,一个类或模块应该只有一个改变的理由,例如“我们计算税收的方式已经改变”,或“我们转换货币的方式已经改变”。


The questions you need to ask yourself are: 您需要问自己的问题是:

  • What is the responsibility of AirlineBooking ? AirlineBooking的责任是AirlineBooking
  • Is updating an airline booking part of that responsibility? 更新航空公司预订部分责任吗?

For example in this case I would say that the responsibility of AirlineBooking is "Encapsulating an airline booking", and that updating an airline booking is in fact the responsibility of the booking system, not AirlineBooking . 例如,在这种情况下,我会说AirlineBooking的责任是“封装航空公司预订”,更新航空公司预订实际上是预订系统的责任,而不是AirlineBooking

Altertnaively, another way of thinking about this is that if I put the Update method on AirlineBooking this would mean that: 另外,另一种思考方式是,如果我将Update方法放在AirlineBooking这意味着:

  • If the booking system changes to use a web service rather than a stored procedure then the AirlineBooking class needs to change. 如果预订系统更改为使用Web服务而不是存储过程,则AirlineBooking类需要更改。
  • If the encapsulation of an airline booking changes (maybe it is possible to suspend a booking, or the name of the airline is now recorded) then AirlineBooking needs to change. 如果航空公司预订的封装发生变化(可能暂停预订,或者现在记录了航空公司的名称),那么AirlineBooking需要更改。

Ie AirlineBooking now has many different reasons to change and so it shouldn't also be responsible for "Updating" AirlineBooking现在有许多不同的原因需要改变,所以它也不应该对“更新”负责


In short, I'd probably do this: 简而言之,我可能会这样做:

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

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

The reason why you should ask yourself these questions is because it does depends on what AirlineBooking is used for in your application. 您应该问自己这些问题的原因是因为它取决于您的应用程序中使用的AirlineBooking

For example, if AirlineBooking is "aware" (ie has a reference to) the booking system then you could add a "helper" method, like this: 例如,如果AirlineBooking是“察觉”(即引用)预订系统,那么您可以添加“帮助”方法,如下所示:

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

Why don't you give your Chicken class a method "Update(some parameters...)"? 你为什么不给你的鸡类一个方法“更新(一些参数......)”? Then, you can just instanciate a chicken by 然后,你可以通过实现一只鸡

Chicken chicken = new Chicken("Name", "descr");

and update by: 并更新:

chicken.Update(myparameters..);

EDIT 编辑

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; }
}

And now you can use your chicken class the following way: 现在您可以通过以下方式使用您的鸡类:

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

Objects should encapsulate functionality. 对象应该封装功能。 Functionality should be passed in to give the encapsulating object flexibility. 应传递功能以使封装对象具有灵活性。

So, if you're saving the chicken, you should pass in the repository functionality. 所以,如果你要保存鸡,你应该传递存储库功能。 If you have chicken calculations, and they're prone to change, it should be passed in as well. 如果你有鸡计算,并且它们很容易改变,那么它也应该被传入。

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

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

Note that how in this example the chicken is able to both perform calculations on itself and persist itself, without having any knowledge of how to perform calculations or persisting things (after all, it's only a chicken). 请注意,在这个例子中,鸡如何能够自己进行计算并坚持自己,而不知道如何进行计算或坚持事物(毕竟,它只是一只鸡)。

While I realize there is no Chicken there might be an Update method on your real object, right? 虽然我意识到没有Chicken ,你的真实对象可能会有一个Update方法,对吗?

I think you should try introduce something else than "update" in terms of language. 我认为你应该尝试在语言方面介绍除“更新”之外的其他内容。 There is no way to really understand what update does. 没有办法真正理解更新的作用。 Does it just update the "data" in the Chicken? 它只是更新鸡肉中的“数据”吗? In that case, what data? 在那种情况下,有什么数据? And also, should you be allowed to update an instance of Chicken like that? 而且,你是否应该被允许像这样更新鸡的实例?

I would rather see stuff like 我宁愿看到类似的东西

chicken.CanFly = false;
if(chicken.CanFly)  // inherited from Bird :)
    ckicken.FlyTo(point);
else
    chicken.WalkTo(point);

Here is a pretty interesting exercise in OOP: http://milano-xpug.pbworks.com/f/10080616-extreme-oop.pdf 这是OOP中一个非常有趣的练习: http//milano-xpug.pbworks.com/f/10080616-extreme-oop.pdf

For multithreaded environment, having a seperate class like ChickenCalculations suits better. 对于多线程环境,像ChickenCalculations这样的单独类更适合。 When you need to perform few other steps besides what chicken.Update() does, you can do that with ChickenCalculations class. 除了chicken.Update()之外,当你需要执行一些其他步骤时,你可以使用ChickenCalculations类来完成。 So if multiple classes that instantiate and call methods on Chicken does not have to worry about the same things that ChickenCalculations class is taking care of. 因此,如果在Chicken上实例化和调用方法的多个类不必担心ChickenCalculations类正在处理的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM