与用于描述模块级别的措辞相比,我对用于描述UpCasting/DownCasting的措辞感到困惑。
想象一下两个类:
public class Human
{
}
public class Doctor : Human
{
}如果我们说到模块的层次,人类类是低级模块,而博士是高层次模块。这是我如何理解,如果从一些帖子有关高层和低级模块(请参阅评论)。
但如果我们想把“人”投给医生,那就太没戏了。如果我们想把医生介绍给人类,那将是一种升华。
这些名字似乎都是土状的。我是不是理解错了什么?
发布于 2021-12-22 15:30:08
没有一个这些链接表明医生比人高。但只有当你相信我们任意的高/低隐喻时,才能理解这一点。
更高的意思是更笼统、更模糊、更抽象。更低的意思是更具体,更精确,更实际。
例如,我们知道医生以什么为生。我们知道他们去上学了。我们知道他们有医学知识。(你们这些博士请安静一会儿)。如果我们只知道我们在谈论的是一个人,我们就不知道那些东西。可能是真的。他们可能不会。
如果我们知道他们是人类,那并不意味着我们什么都不知道。我们知道他们是哺乳动物。我们知道它们是一种碳基生命体。我们知道他们一生中大部分时间都是在交通上度过的。
这个比喻来源于当我们在海拔较高的地方上升时,一些细节会逐渐消失。当人们看上去像蚂蚁时,很难数出他们举起了多少手指。(在这种情况下通常有一两个人,因为人们不喜欢被人看不起)。
在这个高度上,很难判断人类是否穿着实验室的外套和听诊器。但我们还是能看出我们在看的是一个人类。因此,与人类相比,做医生是一个低层次的细节。但是,再高一点,人类和奶牛就会开始看起来一样。甚至是奶牛医生。
至于演员,这决定了我们要怎么想我们要选的东西。当演员把我们从低层次的细节中移开时,我们称它为上播,因为我们将从更高的高度来观察它。当它给出低水平的细节时,它是向下的,因为我们要从较低的高度来观察它。
到目前为止,我已经完全基于类名的语义:Human & Doctor。但是这里也有一个结构参数,因为您的代码具有Doctor从Human继承的特性。我喜欢语义和结构一致的时候。这对大脑来说很简单。而他们就是这样做的。当一个类从另一个类继承时,它很有希望知道它继承的那个类的所有细节。这是有道理的,医生不会因为你仔细观察他们而停止做人。(虽然他们可能会要求你后退一步,戴上面具)。
这个承诺是由编译器强制执行的,它让我们在不同的情况下以不同的方式看待这个人,我们可能也可能不关心他们是否是医生。这种不关心什么时候不重要的能力可以极大地简化我们的代码。
但它肯定会混淆那些认为医生在上面的人。
发布于 2021-12-22 16:13:50
上播与下播指的是类型层次结构中的向上或向下。如果将层次结构想象为树,顶部是最抽象的类型(基类),底部是最具体的类(派生类),那么转换到基类等于“向上”树,而转换到子类就像“向下”转换树。
更高的级别和更低的层次有一点不同--它不是类型的层次结构,而是更多的概念本身。有点难以解释。
暂时忘掉人类/医生的等级制度吧。高级别的内容通常与与应用程序的问题域或目的直接相关的概念有关--与人们将用于的内容有关(例如,确定某些业务工作流中的总体逻辑和步骤的代码)。因此,更高层次的模块更重要。低级别的内容与更一般的技术细节有关(例如,允许您读取文件的通用库,或者管理来自不同优先级来源的读取配置的库)。
现在,假设高层业务工作流中的一个步骤涉及读取类似Excel文档的内容,然后对数据执行操作。此代码将调用通用(低级)模块来读取Excel文件。从这个意义上说,高级模块依赖于低级别的库来进行实际的工作。如果您只是将低级别库包括/导入到高级模块中,并直接调用它,那么它就是一个直接的编译时依赖项。
但是,这会将您绑定到特定的低级别库,您不一定希望这样做。有一种称为依赖反转的技术,它允许您重新调整代码的结构,并使用一些多态性(如继承)来转换该依赖项,并使低级别模块依赖于高级别模块。这允许您(通过一定的计划)使用不同的低级库重用相同的高级代码,例如可以从不同类型的文件中读取的代码。依赖反演是可靠的原则之一。
但这不仅仅是针对特定领域的代码与通用代码之间的关系。高水平和低水平是相对的。因此,即使在特定于域的代码中,您也可以拥有包含总体策略的更高级别的部件,以及相对于此级别较低的部分;这些将代表更多可变的详细信息。
也许一个代码示例可以帮助阐明一些问题,所以请考虑以下几点:
public abstract class DataProcessor
{
// Note that this method defines the high-level policy, which is:
// * read input file
// * calculate averages based on the input, unless the input includes them
// * use the averages (computed or included) to update the internal representation
public void ProcessData()
{
InputData input = this.ReadInputFile();
Averages averages = input.Averages;
if (averages == null)
{
averages = this.CalculateAverages(input);
}
this.UpdateInternalRepresentation(averages);
}
// None of the methods are implemented here, as the application must
// support different implementations for this same overall process
protected abstract InputData ReadInputFile();
protected abstract Averages CalculateAverages(InputData input);
protected abstract void UpdateInternalRepresentation(Averages averages);
}
/*
* The derived classes below are all lower-level modules,
* in relation to the base the class above; they implement
* the details that vary within the overall process
*/
public class JsonDataProcessor : DataProcessor
{
// implements the three abstract methods
}
public class BinaryDataProcessor : DataProcessor
{
// implements the three abstract methods
}请注意,在高级模块中,您几乎可以读取代码中的要点。DataProcessor类从字面上告诉您整个业务流程是什么(正如用户所理解的),而派生类在其中提供了特定的功能。然后,开发人员将根据某些条件选择一个派生(具体)类,并将其作为抽象DataProcessor传递给其他代码。调用代码将通过DataProcessor的公共接口来完成所有的工作,在本例中,这个接口就是ProcessData()方法--但是通常,可能有其他方法,它们可以接受参数等等。
因此,如果您的Human类编码了这样一个高级策略,并且被调用代码以类似的多态方式使用,那么Human是一个比Doctor更高的模块。然后,Human中的代码在概念上比Doctor中的代码更重要;Human中的代码根据用户的业务需求告诉您应用程序实际为用户做了什么。
但是,如果Human只是包含一些由其所有派生类共享的通用人工相关功能,而您只是使用它将该功能包含到您的Doctor类中,并且Human没有意图捕获对您的应用程序(在上述意义上)比Doctor类所提供的任何东西更重要的某种逻辑,而不是一个更高级别的模块(并且,取决于您在其中放入的方法类型,它甚至可能是一个相对较低级别的模块)。
发布于 2021-12-22 16:26:16
类不是实际的事物;它们是抽象的,它们代表了看待事物的可能方式。例如,在这段代码中,我们选择将Doctor视为Human。这不会改变对象本身的性质。
Doctor x = new Doctor();
Human y = x; //It's the same object, just a different way of looking at it 所以我们讨论的是抽象。
更高层次的抽象是一种更具普遍性的抽象,即可以应用于更多的事物。Humans比Doctors更多,因此Human更一般,因此更抽象,因此更高层次。
另一种思维方式是:如果你飞得很高,你也许能分辨出地面上有人类,但却无法分辨他们是医生。一旦你飞得很低,你就能把它们区分开来。
https://softwareengineering.stackexchange.com/questions/435501
复制相似问题