Pull to refresh

Адаптивная типизация

Reading time 3 min
Views 880
Суть сего паттерна заключается в том, чтобы задавать типы значений через накладываемые на них ограничения. И при возможности изменять это значение так, чтобы оно вписывалось в эти ограничения.

Для чего вообще нужна типизация? Чтобы отлавливать появление ошибки как можно раньше, то есть как можно ближе к тому месту, где программист совершил оплошность, а не к тому, где она вызвала непоправимый сбой. Но вручную приводить типы — это слишком накладно, поэтому необходимо автоматическое приведение для совместимых друг с другом типов. Например, «строка не длиннее Х, содержащая только цифры» может быть однозначно преобразована «целое положительное число», и наоборот.

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

Определим несколько типовых тайпкастеров:
function aNumber( $val ){<br>    if( !is_numeric( $val ) ) throw new Exception( 'can not convert to number' );<br>    return $val= +$val;<br>}<br><br>function aString( $val ){<br>    return $val= $val . '';<br>}<br><br>function anArray( $val ){<br>    if( is_object( $val ) ) $val= get_object_vars( $val );<br>    else if( !is_array( $val ) ) throw new Exception( 'can not convert to array' );<br>    return $val;<br>}

Это тайпкастеры общего назвачения. Они делают приведение типов абы как, поэтому лучше вместо них использовать более специфичные типы, соответствующие вашей бизнес-логике:
function anUserId( $val ){<br>    aNumber( &$val );<br>    if( $val <= 0 ) throw new Exception( 'user id is not positive' );<br>    return $val;<br>}

Заметьте, что тайпкастеры сначала изменяют значение параметра, а потом его возвращают. Это позволяет использовать их в двух формах:
function example( $count ){<br>    aNumber( &$count ); // передали переменную по ссылке. внутри функции она при необходимости будет изменена<br>    return aString( $count + 2 ); // передали некоторое выражение и воспользовались возвращаемым функцией результатом.<br>}<br>$x= example( '2' ); // вернёт '4'<br>$y= example( '2x' ); // бросит исключение

Сейчас типовая функция, реализующая паттерн адаптивной типизации, выглядит примерно так:
function xxx( $count= 0, $title= '', $list= array(), $user= 0, $db= null ){<br>    aNumber( &$count ); aString( &$title ); anArray( &$list ); anUserId( &$user ); DB::anInstance( $db );<br><br>    var_dump( $count, $title, $list, $user );<br><br>    return aString( $count * 2 );<br>}

Однако, возможно вскоре в пхп будет добавлена нативная поддержка и запись станет более удобной:
function aString xxx( aNumber $count= 0, aString $title= '', anArray $list= array(), anUserId $user= 0, DB $db= null ){<br>    var_dump( $count, $title, $list, $user );<br><br>    return $count * 2;<br>}

Фактически мы получим тогда статическую типизацию функций с автоматическим приведением и возможностью самостоятельно определять новые типы данных. А пока разве что можно воспользоваться кодогенерацией или не лениться ;-)
Tags:
Hubs:
-4
Comments 18
Comments Comments 18

Articles