I depends what these operations are doing. Let's assume that packing consists of adding fruits to a basket up to a maximum weight, then you will need to know the weight of a fruit in order to make it packable. If you want to pack different kinds of fruits it would be better to have a separate packer class. It feels strange to have fruits packing themselves.
public interface IPackable
{
public float Weight { get; set; }
}
public interface IPacker
{
// Returns a list of packages represented by lists of fruits.
List<List<Fruit>> GetPackages(IEnumerable<Fruit> fruits, float maxPackageWeight);
}
public class Packer : IPacker
{
public List<List<Fruit>> GetPackages(IEnumerable<Fruit> fruits,
float maxPackageWeight)
{
var currentPackage = new List<Fruit>();
var packages = new List<List<Fruit>>(currentPackage);
float currentWeight = 0.0f;
foreach (Fruit fruit in fruits) {
var packable = fruit as IPackable;
if (packable != null && packable.Weight <= maxPackageWeight) {
if (currentWeight + packable.Weight <= maxPackageWeight) {
currentPackage.Add(fruit);
currentWeight += packable.Weight;
} else {
var currentPackage = new List<Fruit>(fruit);
packages.Add(currentPackage);
currentWeight = packable.Weight;
}
}
}
return packages;
}
}
If you add new interfaces for new functionalities, you will not have to change existing interfaces. This is called Interface segregation principle (ISP) and is one of the five SOLID principles of Object-Oriented Design.
Note: Having an IPacker
interface allows you to implement different kinds of packers. An implementation might mix different kinds of fruits within a package, while another one might sort the fruits.