Критика доклада по TDD на конференции 2005 в Киеве

Mihail

Guest
Re: Что для вас TDD (разработка через тестирование)?

По поводу тестов предложенных Вами на PHP конференции.

1. Исходя из того, что свойства объекта можно изменять только через его методы, тесты базируются на тестировании методов класса.

2. Разработчики SimpleTest предлагают свою методику, подход к реализации тестирования кода, но это не значит, что тесты нельзя написать своими руками.

3. Подход который предлагают разработчики SimpleTest не нов, очень уж похож на подход реализации исключений в таких библиотеках как VCL и MFC. Замечу, в SimpleTest и в VCL и MFC можно писать свои исключения.(между прочим, в SimpleTest они так и называются expectation, а базовый класс находится в файле expectation.php)

4. Что касается мастер-класса , а в русском варианте урока(показательный урок специалиста для студентов) мне кажется, что легче было бы использовать пример из файла unit_tester_test.php в каталоге test. На этом примере можно показать не только, как писать свой тест, но и как устроен сам SimpleTest. К тому же этот тест показывает на чем базируется принцип тестирования.
Без всяких лишних слов приведу пример кода:

1. function testAssertEqualReturnsAssertionAsBoolean() {
2. $this->assertTrue($this->assertEqual(5, 5));
3. }
Обратите внимание на строку 2, в особенности $this->assertEqual(5, 5). Как думаете, что выдаст функция? Приведу код ниже.

/**
* Will trigger a pass if the two parameters have
* the same value only. Otherwise a fail.
* @param mixed $first Value to compare.
* @param mixed $second Value to compare.
* @param string $message Message to display.
* @return boolean True on pass
* @access public
*/
function assertEqual($first, $second, $message = "%s") {
return $this->assertExpectation(
new EqualExpectation($first),
$second,
$message);
}

/**
* Runs an expectation directly, for extending the
* tests with new expectation classes.
* @param SimpleExpectation $expectation Expectation subclass.
* @param mixed $test_value Value to compare.
* @param string $message Message to display.
* @return boolean True on pass
* @access public
*/
function assertExpectation(&$expectation, $test_value, $message = '%s') {
return $this->assertTrue(
$expectation->test($test_value),
sprintf($message, $expectation->overlayMessage($test_value)));
}

function test($compare, $nasty = false) {
return (($this->_value == $compare) && ($compare == $this->_value));
}

Конечно (если код правильный) TRUE. ( Я бы разжевал код, да боюсь потратить много ненужных слов.)
Суть происходящего ясна, если нет, то вот она:
Мы задаем методу класса параметры, а потом проверяем результат. Если результат который мы ожидали - значит все хорошо, если нет - надо сделать так чтоб было все хорошо.
Вы спросите:
– А зачем столько накручено;
Я отвечу:
- А бог его знает, так захотели.


Что нам предлагает SimpleTest:

Скачаем его с сайта и изучим исходные тексы. Не буду приводить весь путь вызовов, а приведу основные функции на которых строится работа SimpleTest по тестированию методов класса:

Файл simple_test.php

function assertTrue($result, $message = false) {
if (! $message) {
$message = 'True assertion got ' . ($result ? 'True' : 'False');
}
if ($result) {
$this->pass($message);
return true;
} else {
$this->fail($message);
return false;
}
}


Файл runner.php

/**
* Runs the test methods in the test case.
* @param SimpleTest $test_case Test case to run test on.
* @param string $method Name of test method.
* @access public
*/
function run() {
$methods = get_class_methods(get_class($this->_test_case));
$invoker = &$this->_test_case->createInvoker();
foreach ($methods as $method) {
if (! $this->_isTest($method)) {
continue;
}
if ($this->_isConstructor($method)) {
continue;
}
$this->_scorer->paintMethodStart($method);
if ($this->_scorer->shouldInvoke($this->_test_case->getLabel(), $method)) {
$invoker->invoke($method);
}
$this->_scorer->paintMethodEnd($method);
}
}

function invoke($method) {
set_error_handler('simpleTestErrorHandler');
parent::invoke($method);
$queue = &SimpleErrorQueue::instance();
while (list($severity, $message, $file, $line, $globals) = $queue->extract()) {
$test_case = &$this->getTestCase();
$test_case->error($severity, $message, $file, $line, $globals);
}
restore_error_handler();
}

Краткие пояснения к методу:
function run() - все что мы должны вынести из этой функции, это проход по всем методам класса.
function invoke($method) - все что мы должны вынести из этой функции, это запись журнала времени исполнения.

Если копаться в коде более подробно, конечно можно выяснить дополнительные подробности, но зачем? Все предельно ясно: Идем по методам выполняем их, заполняем журнал результатов, на экран галку.


По поводу проводимого урока и примера в нем.

Взять сторонний продукт и понять принцип его работы, а тем более особенности его использования даже за 1 час не представляется возможным.

Refactoring -improving a computer program by reorganising its internal structure without altering its external behaviour. – это так мимоходом пролетело. :)

Примеры настолько синтетические, что можно застрелиться.
Ведь можно было намного проще взять примеры.

Пока не посмотрел в исходных текстах
/**
* Sets up unit test wide variables at the start
* of each test method. To be overridden in
* actual user test cases.
* @access public
*/
function setUp() {
}
Долго думал – что это такое. Ведь вы ни где не дали объяснений по ней, хотя черным по белому в коде написано, что она выполняет.

Покажите мне хоть один листинг который у реально работает, а не представляет кусок кода. Где у Вас такие строки:
$test = &new GroupTest('This should fail');
$test->addTestFile('test_with_parse_error.php');
$test->run(new HtmlReporter());
Отсюда получается, что большинство примеров я не смогу проверить.

Не один листинг не содержит номера (не говоря уже о номерах строк), как мне Вам задать вопрос по коду?

Вот к примеру возьмем эту часть, страница 158:

class A {
static public function doSomethingComplex() { // Implements comp
}
class B {
public function doSomethingUsefull(){
$result = A :: doSomethingComplex();
if ($result) return $this->_method1();
else return $this->_method2();
}
protected function _method1(){}
protected function _method2(){}
}

class ClassBTest extends TestCase {
function setUp(){
$this->_fixtureForClassA();
$this->_fixtureForClassB();
}
function testMethod1(){
$this->_provideAResult1();
$b = new B();
$this->assert($b->doSomethingUsefull(), $as_method1);
}
function testMethod1(){
$this->_provideAResult2();
$b = new B();
$this->assert($b->doSomethingUsefull(), $as_method2);
}
}

Начну задавать вопросы:
_fixtureForClassA() – что это такое, я иак и не нашел объяснения.
_fixtureForClassB()

testMethod1 – два метода с одинаковым именем, практически везде.

Смотрим дальше, страница 160

/**
* Clears the data set in the setUp() method call.
* To be overridden by the user in actual user test cases.
* @access public
*/
function tearDown() {
}
В тексте нет объяснения, что это такое.

Про объяснение Mock объектов я уже не говорю:

Mock :: generate
Mock :: generatePartial
$b->tally()

А дальше пошло , поехало – Не используйте Singelton используйте Registry и т.д. и т.п.
Мы изучаем шаблоны проектирования или способы тестирования?

Сейчас, я выскажусь про Mock:

/**
* Clones a class' interface and creates a mock version
* that can have return values and expectations set.
* @param string $class Class to clone.
* @param string $mock_class New class name. Default is
* the old name with "Mock"
* prepended.
* @param array $methods Additional methods to add beyond
* those in th cloned class. Use this
* to emulate the dynamic addition of
* methods in the cloned class or when
* the class hasn't been written yet.
* @static
* @access public
*/
function generate($class, $mock_class = false, $methods = false) {
if (! SimpleTestCompatibility::classExists($class)) {
return false;
}
if (! $mock_class) {
$mock_class = "Mock" . $class;
}
if (SimpleTestCompatibility::classExists($mock_class)) {
return false;
}
return eval(Mock::_createClassCode(
$class,
$mock_class,
$methods ? $methods : array()) . " return true;");
}

/**
* Generates a version of a class with selected
* methods mocked only. Inherits the old class
* and chains the mock methods of an aggregated
* mock object.
* @param string $class Class to clone.
* @param string $mock_class New class name.
* @param array $methods Methods to be overridden
* with mock versions.
* @static
* @access public
*/
function generatePartial($class, $mock_class, $methods) {
if (! SimpleTestCompatibility::classExists($class)) {
return false;
}
if (SimpleTestCompatibility::classExists($mock_class)) {
trigger_error("Partial mock class [$mock_class] already exists");
return false;
}
return eval(Mock::_extendClassCode($class, $mock_class, $methods));
}

Хоть бы где по тексту кратко, да нет нигде. Аргументы не убедительны, да и не видел я таких аргументов в пользу использования Mock объектов.

А теперь прочтите, цитирую ( страница 161) «… есть один не протестированный метод, а именно _doSomethingComplex' класса B».

Что в этом случае было создано, а потом протестировано -Mock :: generatePartial('B', 'BTestVersion', array('_doSomethingComplex'));

Вот этот пример(страница 161), по моему мнению, работает не правильно, так как переменная $b имеет тип BTestVersion и имеет как Mock объект только один метод _doSomethingComplex.

function testMethod1(){
$b = new BTestVersion($this);
$b->expectOnce('_doSomethingComplex');
$b->setReturnValue('_doSomethingComplex', $result1);
$this->assert($b->doSomethingUsefull(), $as_method1);
$b->tally();
}

Ух, и это не конец.

Я уже устал, но могу по тексту продолжить.

Если есть желание еще критику выслушать, напишу еще.

С Уважение к авторам статьи.
 

pachanga

Новичок
Re: Re: Что для вас TDD (разработка через тестирование)?

Автор оригинала: Mihail
По поводу тестов предложенных Вами на PHP конференции.

1. Исходя из того, что свойства объекта можно изменять только через его методы, тесты базируются на тестировании методов класса.
Точнее сказать на тестировании интерфейса класса.

2. Разработчики SimpleTest предлагают свою методику, подход к реализации тестирования кода, но это не значит, что тесты нельзя написать своими руками.
Конечно. Если нет желания использовать xUnit, то никто не заставляет, основное преимущество xUnit заключается в том, что разработчику не приходится придумывать колесо для тестовой инфраструктуры.

3. Подход который предлагают разработчики SimpleTest не нов, очень уж похож на подход реализации исключений в таких библиотеках как VCL и MFC. Замечу, в SimpleTest и в VCL и MFC можно писать свои исключения.(между прочим, в SimpleTest они так и называются expectation, а базовый класс находится в файле expectation.php)
Естественно не нов, автор SimpleTest пытался сделать аналог JUnit для PHP. Существует также PHPUnit, который, кстати, намного более полный клон JUnit.

4. Что касается мастер-класса , а в русском варианте урока(показательный урок специалиста для студентов) мне кажется, что легче было бы использовать пример из файла unit_tester_test.php в каталоге test. На этом примере можно показать не только, как писать свой тест, но и как устроен сам SimpleTest. К тому же этот тест показывает на чем базируется принцип тестирования.
Быть может оно и так, однако мне хотелось показать более-менее реальный пример(хотя и примитивный) использования тестирования.

function setUp() {}
Долго думал – что это такое. Ведь вы ни где не дали объяснений по ней, хотя черным по белому в коде написано, что она выполняет.
Будь добр, скачай phpinside последний выпуск и открой последнюю статью "Мастер класс по введению TDD в существующий проект". Найди страницу 178, там написано что такое setUp(), tearDown(). Вообще есть устойчивое понятие фикстура(fixture), собственно установкой/очищением которой setUp(), tearDown() занимаются.

Покажите мне хоть один листинг который у реально работает, а не представляет кусок кода.
Опять же, если не сложно, пролистай вышеупомянутый phpinside и мой мастеркласс.

Вот к примеру возьмем эту часть, страница 158:
...
Начну задавать вопросы:
_fixtureForClassA() – что это такое, я иак и не нашел объяснения.
_fixtureForClassB()
...
Я думаю syfisher тебе лучше прокоментирует, т.к это его материал.

А дальше пошло , поехало – Не используйте Singelton используйте Registry и т.д. и т.п.
Мы изучаем шаблоны проектирования или способы тестирования?
По этому поводу я могу сказать одно, что это прозвучало из уст моего коллеги слишком категорично. Я лично придерживаюсь мнения, что если тот или иной паттерн мешает написанию тестов и можно применить более приемлимый в данной ситуации, то именно так я и поступаю.

Не стоит воспринимать все буквально. Скорее это набор рекомендации нежели свод строгих правил.

Сейчас, я выскажусь про Mock:

Хоть бы где по тексту кратко, да нет нигде. Аргументы не убедительны, да и не видел я таких аргументов в пользу использования Mock объектов.
Надо также понимать, что мы были крайне ограничены в объемах материала и поэтому порой приходилось жертвовать многими листингами и многословными размышлениями в ущерб понятности.

Если есть желание еще критику выслушать, напишу еще.

С Уважение к авторам статьи.
Конечно таковое есть! :) Пиши пожалуйста, что еще не понятно, не понравилось, с чем не согласен.
 

syfisher

TDD infected!!
Модераторы, если можно перенесите три последних сообщения или в новую тему или в тему отзывов по конференции. Ответ Mihail-а, хоть и заслуживает уважению исходя из объема текста, но является offtop-ом для данной темы. Никакого выражения своих мыслей по поводу, что такое для него TDD я не увидел.

Я не знаю, был ли Mihail на коференции или нет, но по раздаточным материалам судить ну никак нельзя. За один-полтора часа нельзя объяснить новичку, что такое моки и где правильно и целесообразно их использовать, плюс объем раздаточных материалов был ограничен. Насколько я помню на конференции было не более 7-8 рук, кто знает и использует модульные тесты в работе и не более 40% тех, кто вообще знаком с концепцией модульного тестирования.

Наши доклады шли один за другим, поэтому понятия Mock, Fixture и т.д. должны быть были понятны из предыдущих выступлений.

Насчет высказывания "мы изучаем методы тестирования или шаблоны проектирования" - смотри тему доклада. Моя цель была - показать, как тесты способны выявлять места сильной связанности классов друг с другом.

Насчет критики 161 страницы - почитай, что же такое частичный мок.

Вообще весь этот пост мне очень напомнил те многочисленные вопросы, которые задавались на конференции не с целью разобраться в сути доклада, а с целью завалить докладчика. Если честно, то я не понял, к чему все это было сказано Mihail-ом.
 

Mihail

Guest
Продолжение

Привет!

Вы написали: «Точнее сказать на тестировании интерфейса класса.»

Не путайте интерфейс с методами класса, вы так создаете путаницу в терминах.

Вы согласны со мной, что в Ваших примерах много ошибок и они не работоспособны?

А так как это уроки, они должны по принципу демонстрировать работоспособность.

1. Вот здесь неумышленная ошибка, стр. 159:
class ComplexDoer(){

2. Вот здесь неумышленная ошибка, стр. 162( на многих примерах эта ошибка повторяется):
class B // implements Server {


3. Попытка выполнить:
<?php

include("./mock_objects.php");

Mock :: generate('Server');

class ClassBTest extends TestCase{
var $a;

function setUp(){
$this->a = new MockComplexDoer($this);
$this->_fixtureForClassB();
}

function tearDown(){$this->a->tally();}
function testMethod1(){
$this->a->expectOnce('doSomethingComplex');
$this->a->setReturnValue('doSomethingComplex', $result1);

$b = new B($this->a);
$this->assert($b->doSomethingUsefull(), $as_method1);
}
function testMethod1() {
$this->a->expectOnce('doSomethingComplex');
$this->a->setReturnValue('doSomethingComplex', $result1);

$b = new B($this->a);
$this->assert($b->doSomethingUsefull(), $as_method1);
}
}

?>

Fatal error: Cannot redeclare testmethod1() in ### on line 23

4. Объясните, пожалуйста, эти загадочные _fixtureForClassA , $this->_provideAResult1();

Вот Ваш пример, немного переделанный для простоты понимания.

Пример № 1

<?php

class A{
function A1(){
printf(" Это функция А1 <BR>");
}
}

class B {

function B1(){
printf(" Это функция B1 <BR>");
printf(" Вызываем метод класса A1 <BR>");
A::A1();
}
}


$b=new B();

$b->B1();

?>

Пример сам по себе не правильный, в данном случае используется особенность PHP. Я думаю, вы согласитесь, не создав экземпляр (объект) класса A вы не можете вызвать его метод? Попробуйте такое проделать в С++, думаю Вам не сделать это! Значит остается два способа:

4.1 Определить указатель на экземпляр объекта типа A, и породить объект ( создать экземпляр) $a=new A();

Пример №2
<?php

class A{
function A1(){
printf(" Это функция А1 <BR>");
}
}

class B {
var $a; // Не обязательно переменная должна быть объявлена здесь.

function B1(){
// var $a; Может быть объявлена и здесь.
printf(" Это функция B1 <BR>");
$a=new A();
printf(" Экземпляр класса А создан, вызываем метод класса A1 <BR>");
$a->A1();
}
}


$b=new B();

$b->B1();

?>

4.2 Передать ссылку на экземпляр объекта типа A созданный заранее.

Пример №3
<?php

class A{
function A1(){
printf(" Это функция А1 <BR>");
}
}

class B {

function B1($a){
printf(" Это функция B1 <BR>");
printf(" Экземпляр класса А создан, вызываем метод класса A1 <BR>");
$a->A1();
}
}

$a=new A();

$b=new B();

$b->B1(&$a);

?>

И где зависимость между TDD и дизайном кода? Что дальше, логике не подчиняется. Зачем создавать класс ComplexDoer, чтоб потом он был родителем для класса А?

5. Забегу немного вперед, есть ссылка на статью о паттерне Registry , Marcus Baker
http://www.phppatterns.com/index.php/article/articleview/75/1/1/ В этой статье есть четкие примеры и последовательность изменений кода.
К тому же в этой статье применяется SimpleTest для тестирования!
Почему для объяснений вы взяли static методы, почему тогда не protect методы?

6. Про Singelton. вопрос спорный? Из статьи приведенной в п.5 и из свойств паттерна Singelton следует следующие: трудность составляет способность сохранить единственный экземпляр объекта. Я думаю, часто приходится иметь единственный экземпляр объекта для сохранения настроек для всего приложения или блока.

7. Вы не последовательны. После главы «Одиночки» класс А теряет своего предка(ComplexDoer), куда? И принимает вот такой вид:

class A {
function method1() {
$b =& B :: instance();
$b->doSomething();
}
}
Что бы это значило? С каких пор он стал вызывать объект класса B, или я чего-то не понял?

8. Ниже приведен кусок кода, где реализация instance()? Я думаю, эта функция и представляет собой важнейшую часть шаблона Registry. И если надо было сделать класс Limb «Одиночкой» то здесь и была корова зарыта!

class Limb{
var $_toolkits = array(array());

function & instance();


9. А вот это порочная практика!
Limb :: registerToolkit($toolkit, 'base');
$tookit =& Limb :: toolkit('base');

Так делать нельзя! Это позор! Я просто поражен. Вы что делаете!

10. Я Вам очень советую почитать Страуструпа «Язык программирования С++». Напишите пару простых шаблонов на С++. Не типизированный язык развращает, а интерпретатор тем более.

11. Глава «Сложность тестирование дочерних классов». Не верно, ничем не сложнее чем любой другой класс! Вот вы делаете Mock::generate или Mock:: generatePartial – так вот наследование ничуть не сложнее. Просто надо знать, что потомок это совершенно новый класс, для которого необходимо писать свой тест! По сути Mock::generate или Mock:: generatePartial создают неявно, подчеркиваю неявно, потомка класса. Это происходит через механизм интерпретации. Когда вы пишете:
Mock :: generatePartial('A', 'ATestVersion', array('_extentionMethod1', '_extentionMethod2'));
Смотрите файл mock_objects.php, function createClassCode там написано следующие $code = "class $mock_class extends $mock_base {\n";
Где $mock_base = «A» и все это через eval().

12. Глава «Наш выбор - делегирование». Абсурд! Я не знаю как с точки тестирования, но с точки ООП я бы мог предположить, легче было сделать так:

interface D {

}

class A implements D
{

class B implements D
{

И тестировать D проще, и понять легче!


Всем кто прочел до конца.

Авторы слишком все усложняют. Все гораздо проще. Если желаете еще комментариев, будет время, напишу. Пожалуйста, высказывайтесь за и против!

По моему мнению, авторы просто до конца не понимают некоторых вещей, как многие из нас. Хоть первый блин комом, я так считаю. Я прошу Всех поддержать дискуссию, как за так и против!

Что касается меня, я хочу определить, нужен мне SimpleTest для тестирования или нет!
 

pachanga

Новичок
Re: Продолжение

Автор оригинала: Mihail
Вы написали: «Точнее сказать на тестировании интерфейса класса.»

Не путайте интерфейс с методами класса, вы так создаете путаницу в терминах.
Очень странно такое слышать. Понятие интерфейса более общее, методы - это те маленькие кирпичики, из которых в конечном итоге строится определенный интерфейс(здесь я говорю об интерфейсе не как о конструкции языка).

Вы согласны со мной, что в Ваших примерах много ошибок и они не работоспособны?
Надо понимать, что в своем материале syfisher использует некоторый метаязык(который очень напоминает php) для примеров. Это чистая теория, не стоит пытаться исполнить этот код.

И хотя я не могу ручаться за код syfisher'а, код моего мастеркласса полностью работоспособный, т.к он более "практический". Если интересно могу выслать проект, на котором велась отработка мастеркласса.

6. Про Singelton. вопрос спорный? Из статьи приведенной в п.5 и из свойств паттерна Singelton следует следующие: трудность составляет способность сохранить единственный экземпляр объекта. Я думаю, часто приходится иметь единственный экземпляр объекта для сохранения настроек для всего приложения или блока.
Ты не понял, трудностью использования паттерна Singleton является то, что порой очень сложно изолировать в тестах код, который его использует. Этот паттерн вводит hardcoded зависимости, от которых сложно избавиться.

8. Ниже приведен кусок кода, где реализация instance()? Я думаю, эта функция и представляет собой важнейшую часть шаблона Registry. И если надо было сделать класс Limb «Одиночкой» то здесь и была корова зарыта!

class Limb{
var $_toolkits = array(array());

function & instance();
Ты опять все перепутал. Это прием используется для того, чтобы потом во время тестирования можно было бы подменять реализацию тулкита на желаемую, более удобную для тестирования.

9. А вот это порочная практика!
Limb :: registerToolkit($toolkit, 'base');
$tookit =& Limb :: toolkit('base');

Так делать нельзя! Это позор! Я просто поражен. Вы что делаете!
Можно аргументировать не эмоциями, а фактами? Сложно понять, что имеется в виду....

10. Я Вам очень советую почитать Страуструпа «Язык программирования С++». Напишите пару простых шаблонов на С++. Не типизированный язык развращает, а интерпретатор тем более.
К чему это было вообще сказано?

11. Глава «Сложность тестирование дочерних классов». Не верно, ничем не сложнее чем любой другой класс!
Никогда не приходилось тестировать класс, который был поочередно отнаследован от 3 и более родителей? Сложность опять же заключается в трудности изоляции тестируемого класса.

Вот вы делаете Mock::generate или Mock:: generatePartial – так вот наследование ничуть не сложнее. Просто надо знать, что потомок это совершенно новый класс, для которого необходимо писать свой тест! По сути Mock::generate или Mock:: generatePartial создают неявно, подчеркиваю неявно, потомка класса. Это происходит через механизм интерпретации. Когда вы пишете:
Mock :: generatePartial('A', 'ATestVersion', array('_extentionMethod1', '_extentionMethod2'));
Смотрите файл mock_objects.php, function createClassCode там написано следующие $code = "class $mock_class extends $mock_base {\n";
Где $mock_base = «A» и все это через eval().
Я ничего не понял из вышесказанного. Можно подробнее, что имелось в виду?

12. Глава «Наш выбор - делегирование». Абсурд!
Mihail, я надеюсь, ты понимаешь, что такое делегирование и чем оно отличается от наследования?

Код:
//наследование
class Foo extends Bar
{
  function do()
  {
    return parent :: baz();
  }
}

//делегирование
class Foo
{
   function do()
   {
      $bar = new Bar();
      return $bar->baz()
   }
}
Я не знаю как с точки тестирования, но с точки ООП я бы мог предположить, легче было сделать так:

interface D {

}

class A implements D
{

class B implements D
{

И тестировать D проще, и понять легче!
Гм...тестировать D, можно пример?

По моему мнению, авторы просто до конца не понимают некоторых вещей, как многие из нас. Хоть первый блин комом, я так считаю. Я прошу Всех поддержать дискуссию, как за так и против!
Мы никогда не претендовали на роль гуру в этой области. Все что у нас есть - небольшой опыт разработки через тестирование, который для нас оказался эффективным и мы хотели бы поделиться этим опытом.

Действительно, мы много чего еще сами до конца не понимаем, однако из твоих комментариев я, к сожалению, пока не услышал действительно конструктивных замечаний по материалу и тех "некоторых вещей".
 

Mihail

Guest
Вы очень краткий товарищ pachanga;)

Все что я Вам хочу сказать это то, что Ваши примеры (сорри syfisher-а) не только запутанны, но и не последовательны. Примеры нельзя проверить, а значит нельзя проверить Вас.

Я жду ответов на мои вопросы, а не кратких отмазок!

1. Я не зря привел ссылку на статью автора библиотеки «SimpleTest» Markus-а Backer-а.
Вы ее смотрели? Ваш пример с классом Limb является шаблоном проектирования Registry , но я не понял что вы хотели с ним сделать?

Если вы смотрели документацию по :: двойному двоеточию вы должны знать, что оно используется :

1.1 можно обращаться к константам, статическим методам ( Здесь все понятно, есть много примеров.)
<?php
class A {
public static $a=’ Это А’;
function printA() {
return $this->a;
}
}
myA= new A();
printf(myA-> printA());
printf(myA-> a); // здесь будет выдана ошибка

?>
1.2 можно обращаться к перегруженным свойствам или методам класса.
<?php
class A {
public static $a=’ Это А’;

function printA() {
return $this->a;
}
}
class B extends A {
function printA() {
return parent::A();
}
}

myB= new B();
printf(myB-> printA());

?>

Теперь посмотрим что у Вас.
Глава «Использование паттерна Registry»

Вы пишете, цитирую:
«Вводится класс Toolkit, …». В примере у Вас нет класса Toolkit, у Вас есть класс BaseToolkit.
«Методы Limb-а статические …». Из примера этого не видно где static?

Я смотрю исходный код Вашего класса:

Метод registerToolkit использует переменную $instance, но она нигде не объявлена, значит когда вы ее создаете, не явно, в методе registrToolkit она так же, неявно, уничтожается после того как метод отработал. Я думаю должно быть $this-> instance.

$instance= &Limb :: instance(); // Что оно вызывает, что оно делает? По моему мнению она работает неправильно.

Дальше смотрим:

$toolkit = new BaseToolkit();
Limb::registrToolkit($toolkit,’base’);

Я считал что сначало надо сделать так:
$toolkit = new BaseToolkit();
$mLimb = new Limb();
$mLimb->registrToolkit($toolkit,’base’);

Я только констатирую факты: -пример не работает, -написан с множеством ошибок, -пример затрудняет понимание, -делаются не верные выводы!

2. Вдруг я узнаю, что вы использовали метаязык? Где вы об этом написали?

3. Вы писали:

/*Ты не понял, трудностью использования паттерна Singleton является то, что порой очень сложно изолировать в тестах код, который его использует. Этот паттерн вводит hardcoded зависимости, от которых сложно избавиться.
*/

Hardcoded – А русским языком объяснить сможете? Вы имели ввиду жесткие связи? Что вы имелли в виду под этим словом?

Не надо прятаться за термины!


4. Вы писали:

/*Mihail, я надеюсь, ты понимаешь, что такое делегирование и чем оно отличается от наследования?*/

делегирование, delegation. При делегировании один объект, ответственный за операцию, передает выполнение этой операции другому объекту. (Г.Буч)

Я не считаю делегирование заменой наследованию. Пример в главе Наш выбор – делегирование» - Теоритически правильный, но опять написан с ошибками.

5. Пришли мне исходный тексты на e-mail , очень интересно!

А то смотрю как баран на стр. 166 и вижу фигу:
$orders_dao=&$toolkit->CreateDAO(‘OrdersDAO’);

Интересно с каких пор $toolkit стал обладателем метода CreateDAO?

-~{}~ 15.08.05 16:18:

Я скачял Ваш проект pachanga.

Вот тот класс LImb - без ошибок.
Почему нельзя было показать его раньше и полностью?
Сколько ошибок было длпущено при его переносе в статью.
pachanga - вы посмотрите разницу, она поразительна!
Вам так ни кажется?

* Copyright 2004 BIT, Ltd. http://limb-project.com, mailto: [email protected]
*
* Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
***********************************************************************************
*
* $Id: Limb.class.php 1392 2005-06-23 14:19:51Z pachanga $
*
***********************************************************************************/
define('LIMB_STATUS_SUCCESS_MASK', 15);
define('LIMB_STATUS_PROBLEM_MASK', 240);

define('LIMB_STATUS_OK', 1);
define('LIMB_STATUS_FORM_SUBMITTED', 2);
define('LIMB_STATUS_FORM_DISPLAYED', 4);

define('LIMB_STATUS_FORM_NOT_VALID', 16);
define('LIMB_STATUS_ERROR', 32);

require_once(LIMB_DIR . '/core/LimbBaseToolkit.class.php');

class Limb
{
static $instance = null;
var $toolkits = array(array());

static function instance()
{
if(!Limb :: $instance)
Limb :: $instance = new Limb();

return Limb :: $instance;
}

static function registerToolkit($toolkit, $name = 'default')
{
Limb :: instance()->toolkits[$name][] = $toolkit;
return $toolkit;
}

static function restoreToolkit($name = 'default')
{
$limb = Limb :: instance();

if(!isset($limb->toolkits[$name]) || sizeof($limb->toolkits[$name]) == 0)
return false;

//without this intermediate $toolkit PHP gets mad
$toolkit = array_pop($limb->toolkits[$name]);
return $toolkit;
}

static function toolkit($name = 'default')
{
$limb = Limb :: instance();

if(!isset($limb->toolkits[$name]) || sizeof($limb->toolkits[$name]) == 0)
return false;

//without this intermediate $toolkit PHP gets mad
$toolkit = end($limb->toolkits[$name]);
return $toolkit;
}

static function saveToolkit($name = 'default')
{
$toolkit = clone(Limb :: toolkit($name));
$toolkit->reset();
Limb :: registerToolkit($toolkit, $name);
return $toolkit;
}
}

Limb :: registerToolkit(new LimbBaseToolkit());//???

?>

А что значит ??? в поле комментария.
Вы в чемто сомневаетесь? ;)
 

pachanga

Новичок
Mihail вы меня просто уели! :) Я даже и не знаю, какие еще аргументы приводить, боюсь мы говорим на разных диалектах, хотя буквы вроде бы и одинаковые.....

P.S. Большая просьба к модераторам, нельзя ли выделить отдельный тред?
 

confguru

ExAdmin
Команда форума
Mihail

Прочитайте правила форума еще раз - не пишите
более 10-20 строк кода, тем более что Вам есть куда
сослаться - конкретную страницу phpinside
 

Mihail

Guest
Прошу прощения.

Но в статье столько ошибок и неточностей, что меньше не получается.


Для pachang-a где в Limb тест на класс db_factory?
Я его не нашел.

Автор оригинала: admin
Mihail

Прочитайте правила форума еще раз - не пишите
более 10-20 строк кода, тем более что Вам есть куда
сослаться - конкретную страницу phpinside
 
Сверху