鸭子类型在软件开发中意味着什么?
当前回答
Duck typing 意味着一个操作没有正式指定其操作数必须满足的要求,而只是使用给定的条件进行尝试。
与其他人所说的不同,这并不一定与动态语言或继承问题有关。
示例任务:在对象上调用某个方法Quack。
如果不使用duck-typing,执行此任务的函数f必须事先指定其参数必须支持某种方法Quack。常用的方法是使用接口
interface IQuack {
void Quack();
}
void f(IQuack x) {
x.Quack();
}
调用f(42)失败,但f(donald)工作,只要donald是一个iquack子类型的实例。
另一种方法是结构类型—但同样,方法Quack()是正式指定的,任何不能提前证明它是嘎嘎作响的东西都会导致编译器失败。
def f(x : { def Quack() : Unit }) = x.Quack()
我们甚至可以写成
f :: Quackable a => a -> IO ()
f = quack
在Haskell中,Quackable类型类确保了方法的存在。
So how does **duck typing** change this?
好吧,正如我所说的,duck输入系统不指定需求,而只是尝试任何可行的方法。
因此,像Python这样的动态类型系统总是使用duck类型:
def f(x):
x.Quack()
如果f得到一个支持Quack()的x,一切正常,否则,它将在运行时崩溃。
但是鸭子类型并不意味着动态类型——事实上,有一种非常流行但完全静态的鸭子类型方法,它也没有给出任何要求:
template <typename T>
void f(T x) { x.Quack(); }
该函数没有以任何方式告诉它想要某个可以Quack的x,所以它只是在编译时尝试,如果一切正常,就没问题。
其他回答
Duck typing:
如果它像鸭子一样说话和走路,那么它就是一只鸭子
这通常被称为诱拐(诱拐推理或也称为归纳,我认为一个更清晰的定义):
从C(结论,我们所看到的)和R(规则,我们所知道的),我们接受/决定/假设P(前提,属性),换句话说,一个给定的事实 ... 医学诊断的基础 和鸭子:C =走路,说话,R =像鸭子,P =它是一只鸭子
回到编程:
对象o有方法/属性mp1和接口/类型T 要求/定义mp1 对象o有方法/属性mp2,接口/类型T要求/定义mp2 ...
因此,不仅仅是简单地接受mp1…在任何对象上,只要它满足mp1的某些定义…,编译器/运行时也应该接受断言o是类型T
上面的例子是这样的吗?Duck输入实质上就是没有输入吗?或者我们应该称之为隐式类型?
它是一个用于没有强类型的动态语言中的术语。
其思想是,为了调用对象上的现有方法,您不需要指定类型——如果在对象上定义了方法,则可以调用它。
这个名字来源于一句话“如果它看起来像鸭子,叫起来像鸭子,那它就是鸭子”。
维基百科有更多的信息。
“鸭子打字”这个词是一个谎言。
“走路像鸭子,叫起来像鸭子,那就是鸭子”这句俗语在这里一次又一次地被重复着。
但这并不是鸭子打字(或者我们通常所说的鸭子打字)的意思。所有我们正在讨论的鸭子打字,是试图强制命令的东西。看看有什么东西是不是嘎嘎叫,不管它说什么。但是并没有推论出这个物体是不是鸭子。
For true duck typing, see type classes. Now that follows the idiom “If it walks like a duck and quacks like a duck then it is a duck.". With type classes, if a type implements all the methods that are defined by a type class, it can be considered a member of that type class (without having to inherit the type class). So, if there is a type class Duck which defines certain methods (quack and walk-like-duck), anything that implements those same methods can be considered a Duck (without needing to inherit Duck).
在编程中,类型可以分为名义类型和结构类型。名义类型考虑类型的整个结构。从谁那里继承的等等。它们显然比结构类型更复杂。例如,在c#和Java中使用名义类型。
结构类型根本不考虑这些问题。对于结构类型,只有单一类型的结构是重要的。这是什么样子。在一个类的例子中。它是否具有相同的参数和预期的类型。这叫做鸭子打字。Duck Typing起源于Duck Test。
Dugtest的意思是:如果某物看起来像鸭子。如果它像鸭子一样游泳。如果它像鸭子一样嘎嘎叫。然后它是一只鸭子。相反地:如果一个测试用例不应用,那么它就不是鸭子。(https://en.wikipedia.org/wiki/Duck_test)
鸭子打字不是类型提示!
基本上,为了使用“duck typing”,你不会针对特定的类型,而是通过使用公共接口来针对更广泛的子类型(不是谈论继承,当我指的子类型时,我指的是适合相同配置文件的“事物”)。
你可以想象一个存储信息的系统。为了读写信息,你需要某种存储空间和信息。
存储类型可以是:文件、数据库、会话等。
无论存储类型是什么,该接口都会让您知道可用的选项(方法),这意味着在这一点上什么都没有实现!换句话说,接口不知道如何存储信息。
每个存储系统都必须通过实现接口的相同方法来知道接口的存在。
interface StorageInterface
{
public function write(string $key, array $value): bool;
public function read(string $key): array;
}
class File implements StorageInterface
{
public function read(string $key): array {
//reading from a file
}
public function write(string $key, array $value): bool {
//writing in a file implementation
}
}
class Session implements StorageInterface
{
public function read(string $key): array {
//reading from a session
}
public function write(string $key, array $value): bool {
//writing in a session implementation
}
}
class Storage implements StorageInterface
{
private $_storage = null;
function __construct(StorageInterface $storage) {
$this->_storage = $storage;
}
public function read(string $key): array {
return $this->_storage->read($key);
}
public function write(string $key, array $value): bool {
return ($this->_storage->write($key, $value)) ? true : false;
}
}
所以现在,每次你需要写/读信息时:
$file = new Storage(new File());
$file->write('filename', ['information'] );
echo $file->read('filename');
$session = new Storage(new Session());
$session->write('filename', ['information'] );
echo $session->read('filename');
在这个例子中,你最终在存储构造函数中使用Duck Typing:
function __construct(StorageInterface $storage) ...
希望能有所帮助;)