Полиморфизм или условные обозначения способствуют лучшему дизайну? - PullRequest
38 голосов
/ 24 октября 2008

Я недавно наткнулся на эту запись в блоге по тестированию Google о правилах написания более тестируемого кода. До этого момента я был согласен с автором:

Предпочитайте полиморфизм условным выражениям: если вы видите оператор переключения, вам следует подумать о полиморфизмах. Если вы видите то же самое, если условие повторяется во многих местах в вашем классе, вы должны снова подумать о полиморфизме. Полиморфизм разделит ваш сложный класс на несколько более простых классов меньшего размера, которые четко определяют, какие части кода связаны и выполняются вместе. Это помогает в тестировании, поскольку простой / меньший класс легче тестировать.

Я просто не могу обернуться вокруг этого. Я могу понять, используя полиморфизм вместо RTTI (или DIY-RTTI, в зависимости от обстоятельств), но это кажется таким широким утверждением, что я не могу себе представить, что он действительно эффективно используется в производственном коде. Скорее, мне кажется, что было бы проще добавить дополнительные тестовые примеры для методов, имеющих операторы switch, чем разбивать код на десятки отдельных классов.

Кроме того, у меня сложилось впечатление, что полиморфизм может привести ко всевозможным тонким ошибкам и проблемам дизайна, поэтому мне любопытно узнать, стоит ли компромисс здесь. Может кто-нибудь объяснить мне, что именно подразумевается под этим руководством по тестированию?

Ответы [ 12 ]

0 голосов
/ 24 октября 2008

Работает очень хорошо если вы понимаете это .

Существует также 2 варианта полиморфизма. Первое очень легко понять в java-esque:

interface A{

   int foo();

}

final class B implements A{

   int foo(){ print("B"); }

}

final class C implements A{

   int foo(){ print("C"); }

}

B и C имеют общий интерфейс. В этом случае B и C не могут быть расширены, поэтому вы всегда уверены, какой метод foo () вы вызываете. То же самое касается C ++, просто сделайте A :: foo чистым виртуальным.

Во-вторых, хитрее полиморфизм во время выполнения. В псевдокоде это выглядит не так уж плохо.

class A{

   int foo(){print("A");}

}

class B extends A{

   int foo(){print("B");}

}

class C extends B{

  int foo(){print("C");}

}

...

class Z extends Y{

   int foo(){print("Z");

}

main(){

   F* f = new Z();
   A* a = f;
   a->foo();
   f->foo();

}

Но это намного сложнее. Особенно, если вы работаете в C ++, где некоторые из объявлений foo могут быть виртуальными, а часть наследования может быть виртуальной. Также ответ на этот вопрос:

A* a  = new Z;
A  a2 = *a;
a->foo();
a2.foo();

может не соответствовать вашим ожиданиям.

Просто внимательно следите за тем, что вы делаете, и не знаете, используете ли вы полиморфизм во время выполнения. Не будьте слишком самоуверенными, и если вы не уверены, что что-то собирается делать во время выполнения, протестируйте это.

0 голосов
/ 24 октября 2008

Это действительно зависит от вашего стиля программирования. Хотя это может быть правильным в Java или C #, я не согласен с тем, что автоматическое решение об использовании полиморфизма является правильным. Вы можете разбить свой код на множество маленьких функций и выполнить поиск в массиве с помощью указателей на функции (например, инициализированных во время компиляции). В C ++ полиморфизм и классы часто используются слишком часто - вероятно, самая большая ошибка проектирования, которую допускают люди, переходящие из сильных языков ООП в C ++, заключается в том, что все входит в класс - это неправда. Класс должен содержать только минимальный набор вещей, которые заставляют его работать в целом. Если нужен подкласс или друг, пусть будет так, но они не должны быть нормой. Любые другие операции над классом должны быть свободными функциями в том же пространстве имен; ADL позволит использовать эти функции без поиска.

C ++ не является языком ООП, не используйте его. Это так же плохо, как программирование на C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...