开发者

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开发者_开发技巧 = 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:

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?

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 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.


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.

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. 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.

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?
  • 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.

Altertnaively, another way of thinking about this is that if I put the Update method on AirlineBooking this would mean that:

  • If the booking system changes to use a web service rather than a stored procedure then the AirlineBooking class needs to change.
  • 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.

I.e. AirlineBooking now has many different reasons to change and so it shouldn't also be responsible for "Updating"


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.

For example, if AirlineBooking is "aware" (i.e. has a reference to) the booking system then you could add a "helper" method, like this:

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?

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


For multithreaded environment, having a seperate class like ChickenCalculations suits better. When you need to perform few other steps besides what chicken.Update() does, you can do that with ChickenCalculations class. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜