2016-11-03 15 views
3

состояние Лисков принцип замещения, которыйЯвляется ли `PartialFunction extends Function` нарушением LSP?

if S is a subtype of T , then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program.

Однако в Scala, есть PartialFunction, что не применяется/определяется во всех случаях.

trait Function1 { 
    def apply(x: A): R 
} 

trait PartialFunction extends Function1 { 
    def apply(x: A): R 
    def isDefinedAt(x: A): Boolean 
} 

Если применить PartialFunction на неопределенное значение, вы получите исключение.

Удобный способ создания PartialFunction в scala - использовать соответствующий шаблон. При этом вы получите MatchError для неопределенных значений.

val fn:Function1[String, Int] = s => s.length 

val pf:PartialFunction[String, Int] = { 
    case "one" => 3 
} 

def program(f:Function1[String, Int], s:String):(Boolean, Int) = (
    f.isInstanceOf[Function1[String, Int]], f(s) 
) 

program(fn, "one") == program(pf, "one") 
program(fn, "two") == program(pf, "two") 

fn: String => Int = <function1>

pf: PartialFunction[String,Int] = <function1>

program: program[](val f: String => Int,val s: String) => (Boolean, Int)

res0: Boolean = true

scala.MatchError: two (of class java.lang.String)

   at scala.PartialFunction$$anon$1.apply(delme.sc:249)

   at scala.PartialFunction$$anon$1.apply(delme.sc:247)

   at ...

Оба fn и pf подтипы Function1, но я не могу заменить fn на pf без изменения моего program. Так что, по-моему, это нарушение LSP.

Как вы думаете?

+0

Это будет прежде всего кусок мнения. У вас есть более конкретный вопрос относительно применения или использования более общего вопроса, который вы задаете? – wheaties

+0

Абсолютно нет. Я просто просил совета у других разработчиков. Может быть, я должен был разместить его в другом сообществе? –

+1

Вы также можете определить 'Function1', который просто генерирует исключения для всех входов, отличных от' 'one ''. Ваш аргумент за нарушение LSP заключается в том, что бросание исключения может быть нежелательным изменением, но 'Function1' все еще может содержать входы, которые генерируют исключения. например 'BigDecimal ("а")'. Основное различие между 'PartialFunction' и' Function1' заключается в том, что у вас есть встроенный способ проверки того, определены ли элементы вначале. –

ответ

3

fn и pf не являются подтипами Function1, так как они не являются типами. Они являются значениями, и LSP не говорит, что вы можете взять любой объект типа T и заменить его на любой объект типа S: Int - это подтип Int, но замена 1 на 2 может привести к неправильной программе.

PartialFunction[String, Int] является подтипом Function1[String, Int], но договор Function1 не запрещает apply от бросающего исключения и в самом деле явно это позволяет:

Apply the body of this function to the argument. It may throw an exception.

Так что, если ваша программа может обрабатывать объекты типа Function1[A, B], он должен иметь дело с apply отбрасывая исключения каким-то образом уже и PartialFunction бросать MatchError - это только конкретный случай.

0

В соответствии с wikipedia существует дополнительная статья, необходимая для применения LSP.

No new exceptions should be thrown by methods of the subtype, except where those exceptions are themselves subtypes of exceptions thrown by the methods of the supertype.

Так (при условии, википедия правильно), нет ни частично определённой функции не нарушает LSP, так как он не относится к данному случаю.

EDIT: Существует дополнительная информация в scaladoc: (Scaladoc Function1)

Note that Function1 does not define a total function, as might be suggested by the existence of PartialFunction. The only distinction between Function1 and PartialFunction is that the latter can specify inputs which it will not handle.

Так еще один способ думать об этом будет то, что пф не является подтипом Fn.

сноска: String => Int

пф: Подмножество String => Int

Дальнейшее редактирование в ответ на комментарий: Нет, я спорю, что LSP не распространяется на всех. Для применения LSP S не должны бросать новые исключения. S здесь делает новые исключения, поэтому LSP не может быть нарушен, поскольку он не применяется.

+0

Но 'PartialFunction' бросает' scala.MatchError', '' '' '' '' does not? Или вы утверждаете, что 'Функция' может что-то бросить? Конечно, исключения здесь не выражены в этом типе, так что это может быть и то и другое. – Bergi

+0

Я добавил еще кое-что. Мои знания об основах теории типов находятся рядом с несуществующими, но поэтому я очень открыт, чтобы быть совершенно неправильным. –

+0

Ах, я понял вашу цитату, поскольку LSP * нарушен * с таким поведением исключений, а не вообще не применялся. – Bergi