我怎么知道何时创建界面?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我怎么知道何时创建界面?相关的知识,希望对你有一定的参考价值。
我正处于开发学习的某个阶段,我觉得我必须更多地了解接口。
我经常阅读它们,但似乎我无法掌握它们。
我已经阅读过这样的例子:动物基类,IAnimal界面,如'Walk','Run','GetLegs'等等 - 但我从来没有做过某些事情,感觉就像“嘿我应该使用界面这里!”
我错过了什么?为什么我要掌握这么难的概念!我只是因为我可能没有意识到一个人的具体需要而感到害怕 - 主要是因为他们理解它们时缺少一些方面!这让我觉得自己在成为开发者方面缺少一些东西!如果有人有过这样的经历并取得了突破,我会很感激如何理解这个概念。谢谢。
它解决了这个具体问题:
你有4种不同类型的a,b,c,d。在你的代码中你有类似的东西:
a.Process();
b.Process();
c.Process();
d.Process();
为什么不让它们实现IProcessable,然后呢
List<IProcessable> list;
foreach(IProcessable p in list)
p.Process();
当你添加50种类型的类都会做同样的事情时,这会扩展得更好。
另一个具体问题:
你有没有看过System.Linq.Enumerable?它定义了大量的扩展方法,它们可以在任何实现IEnumerable的类型上运行。因为实现IEnumerable的任何东西基本上都说“我支持无序foreach类型模式中的迭代”,所以您可以为任何可枚举类型定义复杂行为(Count,Max,Where,Select等)。
完全有可能以.net开发人员的身份度过一生,永远不会编写自己的界面。毕竟,我们在没有它们的情况下幸存下来几十年,我们的语言仍然是图灵完整的。
我不能告诉你为什么需要接口,但我可以列出我们在当前项目中使用它们的位置:
- 在我们的插件模型中,我们通过接口加载插件,并提供插件编写器的接口以符合。
- 在我们的intermachine消息系统中,消息类都实现了一个特定的接口,并使用该接口“解包”。
- 我们的配置管理系统定义了用于设置和检索配置设置的界面。
- 我们有一个接口用于避免令人讨厌的循环引用问题。 (如果你不需要,不要这样做。)
我想如果有一个规则,那么当你想在is-a关系中组合几个类时使用接口,但是你不想在基类中提供任何实现。
一个代码示例(安德鲁与我在what-is-the-purpose-of-interfaces的额外内容的组合),也说明了为什么接口而不是不支持多重继承的语言的抽象类(c#和java):
interface ILogger
{
void Log();
}
class FileLogger : ILogger
{
public void Log() { }
}
class DataBaseLogger : ILogger
{
public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
public void Log() { }
}
请注意,FileLogger和DataBaseLogger不需要接口(可以是Logger抽象基类)。但是请考虑您需要使用强制您使用基类的第三方记录器(假设它暴露了您需要使用的受保护方法)。由于该语言不支持多重继承,因此您将无法使用抽象基类方法。
底线是:尽可能使用界面以获得代码的额外灵活性。您的实施不那么紧密,因此它可以更好地适应变化。
我偶尔使用接口,这是我的最新用法(名称已经推广):
我在WinForm上有一堆需要将数据保存到业务对象的自定义控件。一种方法是分别调用每个控件:
myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);
这个实现的问题是,每当我添加一个控件时,我必须进入我的“保存数据”方法并添加新控件。
我更改了我的控件以实现一个具有方法SaveToBusinessObject(...)的ISaveable接口,所以现在我的“保存数据”方法只是遍历控件,如果找到一个是ISaveable,它会调用SaveToBusinessObject。所以现在当需要一个新控件时,所有人要做的就是在该对象中实现ISaveable(并且永远不要触及另一个类)。
foreach(Control c in Controls)
{
ISaveable s = c as ISaveable;
if( s != null )
s.SaveToBusinessObject(myBusinessObject);
}
接口通常未实现的好处是您可以本地化修改。一旦定义,您很少会更改应用程序的整体流程,但您通常会在详细级别上进行更改。将详细信息保留在特定对象中时,ProcessA中的更改不会影响ProcessB中的更改。 (基础课程也为您带来这种好处。)
编辑:另一个好处是行动的特殊性。就像我的例子一样,我想要做的就是保存数据;我不关心它是什么类型的控件或者它可以做任何其他事情 - 我只想知道我是否可以将数据保存在控件中。它使我的保存代码非常清晰 - 没有检查它是文本,数字,布尔值还是其他因为自定义控件处理所有这些。
一旦您需要强制为您的类行为,您应该定义一个接口。
动物的行为可能涉及步行,进食,跑步等。因此,您将它们定义为界面。
另一个实际示例是ActionListener(或Runnable)接口。当您需要跟踪特定事件时,您将实施它们。因此,您需要在类(或子类)中提供actionPerformed(Event e)
方法的实现。同样,对于Runnable接口,您提供了public void run()
方法的实现。
此外,您可以通过任意数量的类实现这些接口。
使用接口的另一个实例(在Java中)是实现C ++中提供的多重继承。
假设您想要模拟在您尝试入睡时可能发生的烦恼。
接口前的模型
class Mosquito {
void flyAroundYourHead(){}
}
class Neighbour{
void startScreaming(){}
}
class LampJustOutsideYourWindow(){
void shineJustThroughYourWindow() {}
}
正如你清楚地看到,当你试图睡觉时,很多“事情”会令人讨厌。
没有接口的类的用法
但是当谈到使用这些类时,我们遇到了问题。他们没有任何共同之处。您必须单独调用每个方法。
class TestAnnoyingThings{
void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
if(mosquito != null){
mosquito.flyAroundYourHead();
}
if(neighbour!= null){
neighbour.startScreaming();
}
if(lamp!= null){
lamp.shineJustThroughYourWindow();
}
}
}
带接口的模型
为了解决这个问题,我们可以介绍一个iterfaceqazxsw poi
并在类中实现它
interface Annoying{
public void annoy();
}
用于接口
这将使这些类的使用更容易
class Mosquito implements Annoying {
void flyAroundYourHead(){}
void annoy(){
flyAroundYourHead();
}
}
class Neighbour implements Annoying{
void startScreaming(){}
void annoy(){
startScreaming();
}
}
class LampJustOutsideYourWindow implements Annoying{
void shineJustThroughYourWindow() {}
void annoy(){
shineJustThroughYourWindow();
}
}
最简单的例子就是支付处理器(Paypal,PDS等)。
假设您创建了一个具有ProcessACH和ProcessCreditCard方法的接口IPaymentProcessor。
您现在可以实现具体的Paypal实现。使这些方法调用PayPal特定的功能。
如果您稍后决定需要切换到其他提供商,则可以。只需为新提供程序创建另一个具体实现。由于你所依赖的只是你的界面(契约),你可以换掉你的应用程序使用哪一个而不用改变消耗它的代码。
它还允许您执行模拟单元测试(.Net)。如果您的类使用接口,您可以在单元测试中模拟对象并轻松测试逻辑(实际上不会访问数据库或Web服务等)。
class TestAnnoyingThings{
void testAnnoyingThinks(Annoying annoying){
annoying.annoy();
}
}
如果浏览.NET Framework程序集并深入查看任何标准对象的基类,您会注意到许多接口(名为ISomeName的成员)。
接口基本上用于实现大小的框架。在我想编写自己的框架之前,我对接口有同感。我还发现理解接口帮助我更快地学习框架。在您想为几乎任何事情编写更优雅的解决方案的那一刻,您会发现界面非常有意义。这就像是让一个班级为这份工作穿上合适衣服的方法。更重要的是,接口允许系统变得更加自我记录,因为当类实现接口时,复杂对象变得不那么复杂,这有助于对其功能进行分类。
当类希望能够显式或隐式地参与框架时,它们实现接口。例如,IDisposable是一个通用接口,为流行且有用的Dispose()方法提供方法签名。在一个框架中,您或其他开发人员需要知道的关于类的所有内容是,如果它实现了IDisposable,那么您知道((IDisposable)myObject).Dispose()可用于清理目的。
CLASSIC示例:如果不实现IDisposable接口,则不能在C#中使用“using()”关键字构造,因为它要求将指定为参数的任何对象隐式转换为IDisposable。
复杂示例:更复杂的示例是System.ComponentModel.Component类。此类实现IDisposable和IComponent。大多数(如果不是全部)具有与之关联的可视化设计器的.NET对象实现IComponent,以便IDE能够与组件交互。
结论:随着您越来越熟悉.NET Framework,在对象浏览器或.NET Reflector(免费)工具(http://www.nmock.org/)中遇到新类时,您要做的第一件事就是检查它继承了哪个类来自它以及它实现的接口。 .NET Reflector甚至比对象浏览器更好,因为它还允许您查看Derived类。这允许您了解从特定类派生的所有对象,从而可能了解您不知道存在的框架功能。当更新或新的命名空间添加到.NET Framework时,这一点尤为重要。
考虑一下你是第一人称射击游戏。该玩家有多种枪可供选择。
我们可以有一个接口http://www.red-gate.com/products/reflector/,它定义了一个函数Gun
。
我们需要shoot()
类的不同子类,即Gun
ShotGun
等。
Sniper
射手类
射手拥有他盔甲中的所有枪支。让我们创建一个ShotGun implements Gun{
public void shoot(){
\shotgun implementation of shoot.
}
}
Sniper implements Gun{
public void shoot(){
\sniper implementation of shoot.
}
}
来代表它。
List
当需要时,使用List<Gun> listOfGuns = new ArrayList<Gun>();
功能,射手在他的枪中循环
switchGun()
我们可以使用上面的函数设置当前的Gun,并在调用public void switchGun(){
//code to cycle through the guns from the list of guns.
currentGun = //the next gun in the list.
}
时简单地调用shoot()
函数。
fire()
拍摄功能的行为将根据public void fire(){
currentGun.shoot();
}
界面的不同实现而变化。
结论
当一个类函数依赖于另一个类的函数时,创建一个接口,该函数根据实现的类的实例(对象)进行更改。
例如来自Gun
类的fire()
函数期望枪支(Shooter
,Sniper
)实现ShotGun
功能。所以,如果我们切换枪和火。
shoot()
我们改变了shooter.switchGun();
shooter.fire();
函数的行为。
扩展拉森纳所说的话。接口是所有实现类必须遵循的合同。因此,您可以使用称为编程的技术来签订合
以上是关于我怎么知道何时创建界面?的主要内容,如果未能解决你的问题,请参考以下文章