Программирование на интерфейс
Во многих книгах по дизайну я встречал фразу "программа для интерфейса, а не для реализации". Мы могли бы устранить тесную связь, придерживаясь этого, и мы также можем добиться абстракции. У меня есть сценарий, и я хотел бы реализовать его, но не знаю, насколько я знаю.
У меня был класс и некоторый набор свойств. Через некоторое время мое требование изменилось и я вынужден был добавить два свойства с именем ItemAssociatedToProduct
а также ItemAssociatedToOffers
, Создание объекта с этими свойствами заставляет меня волноваться и решил разделить класс, имея Offer
класс и Product
класс, наследующий Item
учебный класс. Я думал исключить конкретную реализацию моего объекта. Так что в моем классе пользовательского интерфейса я хочу создать такой объект,
Item myItem = new Product() // or Offer()
Так что я могу достичь абстракции. Но поскольку у класса Item нет дополнительных свойств, которые есть у производного класса, я не могу на него ссылаться. Я подумал, что должен использовать проверку "is" для определения типа и приведения типа к соответствующему объекту. Но снова его бьют вокруг куста. Разве это не способ решить мой сценарий, но в то же время придерживаться абстракции или этот принцип предназначен только для использования его для поведения (метод / функция), а не для данных (свойства)?
Адам Лисс -> теперь это правильно:)?
class Program
{
static void Main(string[] args)
{
Item myProduct = new Product();
myProduct.ProductName = "CellPhone";
myProduct.ProductPrice = "$300";
myProduct.ItemType = myProduct.GetProductOrOffer();
Item myOffer = new Offer();
myOffer.ProductName = "CellPhoneCover";
myOffer.ProductPrice = "$0";
myOffer.ItemType = myOffer.GetProductOrOffer();
}
}
interface Item
{
String GetProductOrOffer();
String ProductName
{
get;
set;
}
String ProductPrice
{
get;
set;
}
String ItemType
{
get;
set;
}
}
class Product : Item
{
public String GetProductOrOffer()
{
return "Product";
}
public String ItemType
{
get;
set;
}
public string ProductName
{
get;
set;
}
public string ProductPrice
{
get;
set;
}
}
class Offer : Item
{
public String GetProductOrOffer()
{
return "Offer";
}
public String ItemType
{
get;
set;
}
public string ProductName
{
get;
set;
}
public string ProductPrice
{
get;
set;
}
}
2 ответа
Когда вы программируете интерфейс, вы создаете контракт, в котором говорится, что каждый реализующий класс будет предлагать определенный набор методов. Это означает, что любой может написать общий код, который работает на Item
, используя только свойства в интерфейсе, и не беспокоиться о конкретном типе Item
это.
Это может быть легче понять на примере.
interface Item {
getItemType();
getName();
getDescription();
getPrice();
}
class Product implements Item {
getItemType() { return "Product"; }
getName() { /* implementation here */ }
getDescription() { /* implementation here */ }
getPrice() { /* implementation here */ }
getManufacturer() { /* implementation here */ }
getWholesaleCost() { /* implementation here */ }
}
class Offer implements Item {
getItemType() { return "Offer"; }
getName() { /* implementation here */ }
getDescription() { /* implementation here */ }
getPrice() { /* implementation here */ }
getExpirationDate(); { /* implementation here */ }
getFinePrint(); { /* implementation here */ }
}
Теперь вы можете создавать переменные типа Product
а также Offer
, Поскольку вы их создаете, вам, вероятно, все равно, какой тип они имеют, поэтому храните их в соответствующих классах. На самом деле, вы могли бы даже реализовать общие методы по-другому, потому что, возможно, цена Offer
зависит от дня недели, но цена Product
зависит от его оптовой стоимости, поэтому вам придется рассчитывать цены по-разному.
Но любой может также написать общий метод, который заботится только о том, чтобы его аргумент Item
, как это:
main() {
Product myProduct = new Product();
myProduct.name = "SuperExtraPolyWidget";
myProduct.description = "The best widget you can buy!";
myProduct.price = 1428.57;
myProduct.manufacturer = "SuperExtra, Inc.";
myProduct.wholesaleCost = 314.15;
Offer myOffer = new Offer();
myOffer.name = "WonderBargainDeal";
myOffer.description = "The deal you've wondered about";
myOffer.price = 0;
myOffer.expirationDate = Date(yesterday);
myOffer.finePrint = "Valid between 1:23 and 1:24 AM February 29.";
List<Item> itemList = List.of(myProduct, myOffer);
printCatalog(itemList);
}
printCatalog(List<Item> items) {
for (Item item in items) {
printf("%s: %s\n", item.getItemType(), item.getName());
printf("------------------\n");
printf("%s\n\n", item.getDescription());
}
}
Это может распечатать каталог из списка Product
с или Offer
s (или оба), потому что он использует только методы, которые определены в Item
интерфейс.
Важно использовать наследование, только если оно имеет смысл и является подходящим способом моделирования объектов домена. Продукт и Предложение должны реализовывать Предмет только в том случае, если существует отношение "есть": Продукт - это Предмет, а Предложение - это Предмет.
Это тот случай? Не зная деталей вашего заявления, мы не знаем наверняка. Но я работал над некоторыми коммерческими приложениями, где Item представлял собой конкретную позицию заказа для общего типа Продукта, и в этих случаях Продукты вообще не были Элементами. Элемент может иметь атрибут Product, относящийся к конкретному типу Product, который представляет этот Item. Аналогично для предложений - если Предложение не является Предметом, это должен быть отдельный класс, и, возможно, Предметы будут иметь атрибут, который связывает его с Предложениями, используемыми для покупки этого Предмета.
Теперь, если в вашей системе продукт и предложение действительно являются элементами, то, вероятно, это правильный способ их моделирования. Вам просто нужно дать Предмету соответствующие методы, которые могут быть реализованы как Продуктом, так и Предложением. И могут быть некоторые случаи, когда вам нужно сыграть, мы все сталкивались с такими ситуациями раньше.
В заключение, просто убедитесь, что ваше моделирование соответствует тому, что представляют эти объекты.