Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >OOP:从不可变对象继承

OOP:从不可变对象继承
EN

Stack Overflow用户
提问于 2021-05-02 01:52:23
回答 1查看 110关注 0票数 1

背景

假设我有一组相互关联的字段,因此我创建了一个类来收集它们。让我们把这个类称为Base。还有一些方法,这些方法在所有派生类中都是通用的。另外,让我们假设我们希望Base及其所有派生类是不可变的。

在不同的上下文中,这些字段支持额外的操作,因此我有不同的派生类,它们继承字段并根据它们的上下文提供额外的方法。让我们把这些叫做Derived1Derived2等等。

在某些情况下,程序需要派生类的实例,但是字段的状态必须满足某些条件。因此,我做了一个类RestrictedDerived1,在调用它的基本构造函数之前,它确保在构造函数中满足条件(或者更改参数以符合它的要求),或者抛出一个错误。

此外,在某些情况下,我甚至需要满足更多的条件,所以我有了SuperRestrictedDerived1。(附带注意:如果满足了某些条件,这个类可以更有效地计算某些事情,因此它覆盖了Derived1的一些方法。)

问题

到目前一切尚好。问题是,所有这些类的大多数方法都涉及在这个层次结构中创建一个类的另一个实例(不总是与调用方法的实例相同,但通常是相同的实例),而是进行了一些修改,这些修改可能涉及到一些复杂的计算(即不只是改变一个字段)。例如,Derived1的一种方法可能如下所示:

代码语言:javascript
运行
AI代码解释
复制
public Derived1 foo(Base b) {
    TypeA fieldA = // calculations using this and b
    TypeB fieldB = // more calculations
    // ... calculate all fields in this way
    return new Derived1(fieldA, fieldB, /* ... */);
}

但是,层次结构RestrictedDerived1需要这个相同的函数来返回自身的一个实例(如果不能实例化它显然会抛出一个错误),所以我需要像这样覆盖它:

代码语言:javascript
运行
AI代码解释
复制
@Override
public ResrictedDerived1 foo(Base b) {
    return new RestrictedDerived1(super.foo(b));
}

这需要一个副本构造函数,并不必要地分配一个将立即销毁的中间对象。

可能解决办法

我想到的另一种解决方案是将一个函数传递给这些方法中的每一个,这些方法构造了某种类型的Base,然后函数看起来如下所示:

代码语言:javascript
运行
AI代码解释
复制
// In Derived1
public Derived1 foo(Base b, BaseCreator creator) {
    TypeA fieldA = // calculations using this and b
    TypeB fieldB = // more calculations
    // ... calculate all fields in this way
    return creator.create(fieldA, fieldB, /* ... */);
}

public Derived1 foo(Base b) {
    return foo(b, Derived1::create);
}

public static Derived1 create(TypeA fieldA, TypeB fieldB, /* ... */) {
    return new Derived1(fieldA, fieldB, /* ... */);
}

// In RestrictedDerived1
@Override
public ResrictedDerived1 foo(Base b) {
    return (RestrictedDerived1) foo(b, RestrictedDerived1::create);
}

public static RestrictedDerived1 create(TypeA fieldA, TypeB fieldB, /* ... */) {
    return new RestrictedDerived1(fieldA, fieldB, /* ... */);
}

我的问题

这是可行的,但我觉得“笨重”。我的问题是,是否有一些设计模式或概念,或替代设计,以方便我的情况?

我试过使用泛型,但很快就变得凌乱不堪,而且不能很好地应用于不止一个级别的继承。

顺便说一下,它们所引用的实际类是3D点和向量。我有一个名为Triple的基,它包含x、y和z(以及一些函数,这些函数接受lambda并将它们应用于每个坐标,并构造一个新的三元组并得到结果)。然后,我有一个带点相关函数的派生类Point,还有一个带有函数的派生类Vector。然后我有了NonZeroVector (扩展Vector),它不是零向量(因为需要向量的其他对象有时需要保证它不是零向量,而且我不想到处检查)。此外,我还有NormalizedVector (扩展NonZeroVector),它保证长度为1,并在构建时将自己规范化。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-02 11:10:11

MyType

可以使用MyType、this类型或self类型的概念来解决这一问题。基本思想是MyType是运行时派生最多的类型。您可以将它看作是this的动态类型,但是静态地(在“编译时”)引用。

不幸的是,并不是很多主流编程语言都有MyTypes,但例如TypeScript有,而且我被告知拉库也有。

在TypeScript中,您可以通过将foo的返回类型设置为MyType (在TypeScript中拼写为this )来解决问题。看起来会是这样的:

代码语言:javascript
运行
AI代码解释
复制
class Base {
    constructor(public readonly fieldA: number, public readonly fieldB: string) {}

    foo(b: Base): this {
        return new this.constructor(this.fieldA + b.fieldA, this.fieldB + b.fieldB);
    }
}

class Derived1 extends Base {
    constructor(fieldA: number, fieldB: string, protected readonly repeat: number) {
        super(fieldA * repeat, fieldB.repeat(repeat));
    }

    override foo(b: Base): this {
        return new this.constructor(
            this.fieldA + b.fieldA, this.fieldB + b.fieldB, this.repeat
        );
    }
}

class RestrictedDerived1 extends Derived1 {
    constructor(fieldA: number, fieldB: string, repeat: number) {
        super(fieldA * repeat, fieldB.repeat(repeat), repeat);
        if (repeat >= 3) { 
            throw new RangeError(`repeat must be less than 3 but is ${repeat}`)
        }
    }
}

const a = new RestrictedDerived1(23, 'Hello', 2);
const b = new Base(42, ' World');

const restrictedDerived = a.foo(b); // Inferred type is RestrictedDerived1

微b0rken游乐场链接

隐工厂

在具有类型类或隐式(如Scala)的语言中,您可以使用隐式Factory对象解决问题。这类似于您对创建者的第二个示例,但不需要在任何地方显式地传递创建者。相反,语言会含蓄地召唤他们。

实际上,您的需求非常类似于Scala集合框架的核心需求之一,即您希望像mapfilterreduce这样的操作只实现一次,但仍然保留集合的类型。

大多数其他集合框架只能实现其中的一个目标:例如,Java、C#和Ruby,每个操作只有一个实现,但它们总是返回相同的、最通用的类型(Stream在Java中,IEnumerable在C#中,Array在Ruby中)。Smalltalk的集合框架保留了类型,但对每个操作都有重复的实现。一个非复制的、类型保持的集合框架是抽象设计人员/语言设计人员的圣杯之一。(这么多介绍面向对象的新方法的论文使用Smalltalk的重构作为它们的工作示例,这绝非巧合。)

F-有界多态性

如果没有可用的MyType或隐式构建器,则可以使用F-有界多态性

典型的例子是如何设计Java的clone方法:

代码语言:javascript
运行
AI代码解释
复制
interface Cloneable<T extends Cloneable<T>> {
    public T clone();
}

class Foo implements Cloneable<Foo> {
    @Override
    public Foo clone() {
        return new Foo();
    }
}

JDoodle实例

然而,对于深嵌套的继承层次结构来说,这很快就变得单调乏味了。我试着在Scala,但我放弃了中建模。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67354981

复制
相关文章
ubuntu的ssh连不上_ubuntu网络连接没有显示出来
之前发在其他的博客上的,现在移动以下位置 之前的链接: http://blog.chinaunix.net/uid-69944074-id-5831708.html(原创文章) 使用Ubuntu,经常需要需要SSH远程连接,但是有时候会出现问题,难以捉摸,下面参考别人的,在结合自己的尝试总结下吧。 服务器配完ubuntu系统以及LNMP环境以后,想用WINSCP远程登录,就需要开启SSH服务才能支持。
全栈程序员站长
2022/09/21
2.6K0
c#利用Halcon的图像显示控件显示图像
?
vv彭
2020/10/27
3K0
c#利用Halcon的图像显示控件显示图像
opencv显示图像
如果说我们在些项目的过程中会频繁的查看我们的图片,我们就可以直接给它封装一个方法:
淼学派对
2023/10/14
2750
opencv显示图像
MATLAB的图像显示方法
在 MATLAB 中, 序列是用矩阵向量表示, 但它没有包含采样信息, 即序列位置信息, 为 此, 要表示一个序列需要建立两个向量; 一是时间序列 n , 或称位置序列, 另一个为取值序 列 x ,表示如下:
timerring
2023/03/04
4.9K0
MATLAB的图像显示方法
基于FPGA的图像显示
这几天一直在调试FPGA的图像显示系统,今天终于成功,图像不在闪烁,也不再边框缺失。
FPGA开源工作室
2019/10/29
1.9K0
基于FPGA的图像显示
图像特效显示(下)
移动是将图像看作一个整体,显示时不能像扫描那样,扫描方式有些像打开一副画,例如显示上部分的时候,下部分可以不现实,而移动则可以看成一块木板画,显示时必须按物理顺序进行,例如从上向下平移时,必须先显示下面的图像,后显示上面的图像,因此平移的算法比扫描难一些。
周旋
2022/08/07
1K0
图像特效显示(下)
图像特效显示(上)
准备11月份更一个新的系列,之前看的杨淑莹老师的《数字图像处理Visual Studio C++技术实现》,里面的代码都没来得及打,而且其是基于自定义的图像类实现的,这个系列就把所有例程移植为opencv-C++实现,也就是算法逻辑用C++实现,图像对象使用opencv自带的图像类。
周旋
2022/08/07
1.1K0
图像特效显示(上)
【图像篇】OpenCV图像处理(二)---图像读取与显示
在上一篇文章中,我们简要介绍了图像的基础知识,包括图像彩色通道,像素,分辨率等知识,学会这些东西,我们才能更好的理解图像处理的各种操作,今天,我们将会用上一篇文章(【图像篇】opencv图像处理(一)---图像基础知识)提到的工具--OpenCV,并用python语言调用OpenCV接口来进行实际的代码操作,一起来看看吧!
用户5410712
2022/06/01
1.2K0
【图像篇】OpenCV图像处理(二)---图像读取与显示
【OpenCV】图像的读入与显示
相关工程文件下载: 链接:https://pan.baidu.com/s/1jfDQTdOQqIf34-D5Nx6big 密码:0d13
EdenChen
2018/04/17
1.7K0
【OpenCV】图像的读入与显示
圆形收缩显示图像效果
<!doctype html> <html> <head> <style> div{width:300px;height:300px;border-radius:50%;overflow:hidden;position:relative;} div img{width:100%;height:100%;} div span{position:absolute;left:0;top:0;width:100%;height:100%; border-color:black; border-style:sol
用户1730674
2018/05/02
1.5K0
圆形收缩显示图像效果
创建maven项目没有显示项目结构
有一些同学创建Maven项目尤其是从git库中拉取了Maven项目之后没有显示项目结构,该怎么办??
明明如月学长
2021/08/31
1.1K0
创建maven项目没有显示项目结构
Sixel:改变终端图像显示的革命
Sixel 源自“六个像素”(six pixels),是一种用于计算机终端中图像显示和传输的编码方法。最初广泛应用于计算机终端领域的图形和图像显示,Sixel 将图像划分为块,每个块由六个像素组成。这些图像块被表示为单个字符,这些字符可以是 ASCII 字符或其他字符集中的字符。
程序那些事儿
2023/10/14
1.1K0
Sixel:改变终端图像显示的革命
主动发送指令,但是没有反应,日志显示鉴权失败解决办法
这里少了一个空格
快乐的小白
2023/04/06
5540
主动发送指令,但是没有反应,日志显示鉴权失败解决办法
opencv videocapture参数_opencv显示图像
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/17
1.4K0
python PIL 打开\显示\保存图像
使用python进行数字图片处理,还得安装Pillow包。虽然python里面自带一个PIL(python images library), 但这个库现在已经停止更新了,所以使用Pillow, 它是由PIL发展而来的。
狼啸风云
2020/10/28
3.5K0
python PIL 打开\显示\保存图像
(译)SDL编程入门(2)在屏幕上显示图像
注意:从现在开始,教程将只涉及源代码的关键部分。如果想看完整的程序,你必须下载完整的源码。
arcticfox
2020/09/24
2.8K0
OpenCV中图像的读取,显示与保存
相关函数:cv2.imread()、cv2.imshow()、cv2.imwrite()
py3study
2020/01/19
3.1K0
Python+OpenCV的图像读取、显示、保存
一、图像的读取 图像的读取主要函数是cv2.imread()。 函数格式:Mat cv::imread (const String & filename, int flags = IMREAD_COLOR) 功能:读取图片文件。 参数: windows位图:后缀名为bmp JPEG文件:后缀名为jpeg/jpg JPEG2000:后缀名为jp2 便携式网络图像文件:后缀名为png TIFF文件:后缀名为tiff/tif 参数二是整型的flag,标志,默认值为IMREAD_COLOR,取值有如下几种: IMREAD_UNCHANGED:如果设置,则按原样返回加载的图像(带有Alpha通道,否则会被裁剪)。 IMREAD_GRAYSCALE:如果设置,总是将图像转换为单通道灰度图像读入。 IMREAD_COLOR:如果设置,总是将图像转换为3通道BGR彩色图像读入。 IMREAD_ANYDEPTH:如果设置,当输入具有相应深度时返回16位/ 32位图像,否则将其转换为8位。 IMREAD_ANYCOLOR:如果设置,图像将以任何可能的颜色格式读取。 IMREAD_LOAD_GDAL:如果设置,总是使用GDAL驱动程序加载图像。 IMREAD_REDUCED_GRAYSCALE_2:如果设置,总是将图像转换为单通道灰度图像,图像尺寸减小1/2。 IMREAD_REDUCED_COLOR_2:如果设置,总是将图像转换为3通道BGR彩色图像,图像尺寸减小1/2。 IMREAD_REDUCED_GRAYSCALE_4:如果设置,总是将图像转换为单通道灰度图像,图像尺寸减小1/4。 IMREAD_REDUCED_COLOR_4:如果设置,总是将图像转换为3通道BGR彩色图像,图像尺寸减小1/4。 IMREAD_REDUCED_GRAYSCALE_8:如果设置,总是将图像转换为单通道灰度图像,图像尺寸减小1/8。 IMREAD_REDUCED_COLOR_8:如果设置,总是将图像转换为3通道BGR彩色图像,图像尺寸减小1/8 常用的是前三种。因为flags是整型,所以传入数值也行: flags >0:等同于IMREAD_COLOR。 flags =0:等同于 IMREAD_GRAYSCALE。 flags <0: 等同于IMREAD_UNCHANGED。 通常是给1、0、-1,给其他整型也是可以的。 返回值:Mat类型。从opencv2开始,用于存放图像的数据类型就是Mat, 二、图像的显示 图像读取后,下一步就是再把图像显示出来,主要函数有:cv2.namedWindows()、cv2.imshow()。再另外再介绍三个函数cv2.waitKey()、cv2.destroyWindow()、cv2.destroyAllWindows()。 2.1 cv2.namedWindows函数介绍 void cv::namedWindow (const String & winname,int flags = WINDOW_AUTOSIZE ) 功能:创建一个窗口。 参数:参数一是winname,给创建的窗口起一个名字,以后通过这个名字调用该窗口;参数二整型的flags,定义窗口的属性,默认值是WINDOW_AUTOSIZE,其他取值如下所示: WINDOW_NORMAL:用户可以调整窗口大小(不受约束)/也可以使用将全屏窗口切换为正常大小。 WINDOW_AUTOSIZE:用户无法调整窗口大小,窗口大小随显示图像的大小而变化。 WINDOW_OPENGL:带有opengl支持的窗口。 WINDOW_FULLSCREEN:将窗口更改为全屏。 WINDOW_FREERATIO:不遵循图像的比例调整图像后在窗口显示 WINDOW_KEEPRATIO:根据图像的比例调整图像后在窗口中显示 2.2 cv2.imshow函数介绍 void cv::imshow (const String & winname, InputArray mat ) 功能:在指定窗口显示图像。 参数:参数一是窗口名;参数二设置为要显示的图像。 注意此函数之后应该跟随函数waitKey,指定窗口显示多少毫秒。 2.3 cv2.waitKey函数介绍 int cv::waitKey (int delay = 0) 功能:等待按键或延迟多少毫秒。 参数:整型的delay,默认值是0。设置为0表示永久等待按键,设置为非零,表示延迟delay毫秒。该函数仅在创建至少一个窗口并且窗口处于活动状态时才起作用。 2.4 cv2.destroyWind
菲宇
2019/11/12
3.8K0
点击加载更多

相似问题

当我想让它扭曲时,图像没有显示出来

12

图像没有显示出来

10

图像没有显示出来

24

JPG图像没有显示在反应Js中

14

反应:没有显示图像

35
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档