当前位置: 首页 > 知识库问答 >
问题:

前端 - 我们看到很多面向对象编程的优秀的项目,为何能设计的非常好呢?

陶健
2024-08-11

我们看到很多面向对象编程的优秀的项目,

我们看到一个类继承一个类地,并且传递的类实例,看起来是相当庞大的项目,拥有相当多设计好的类,并且每个类设计的都很好:
QQ20240809-222225.png

但是比如我这等初级程序员,设计项目的时候,
1、根本很难把一个类设计的很好
2、想不到很多的接口继承或类实现接口的设计
3、比如在设计这个类的时候:

class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider {

    public constructor(
        private readonly client: ITypeScriptServiceClient,
        private readonly cachedResponse: CachedResponse<Proto.NavTreeResponse>,
    ) { }

    public async provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.DocumentSymbol[] | undefined> {
        const file = this.client.toOpenTsFilePath(document);
        if (!file) {
            return undefined;
        }

        const args: Proto.FileRequestArgs = { file };
        const response = await this.cachedResponse.execute(document, () => this.client.execute('navtree', args, token));
        if (response.type !== 'response' || !response.body?.childItems) {
            return undefined;
        }

        // The root represents the file. Ignore this when showing in the UI
        const result: vscode.DocumentSymbol[] = [];
        for (const item of response.body.childItems) {
            TypeScriptDocumentSymbolProvider.convertNavTree(document.uri, result, item);
        }
        return result;
    }

1)不会想到:private readonly 这些字段的功能,不会纳入类设计
2)不会想到泛型的利用

private readonly cachedResponse: CachedResponse<Proto.NavTreeResponse>

3)比如这个参数:是有固定的类依据,如果是我的话,会可能直接想到传输string或者number这样的初级的类型:这些优秀的软件工程师为何就能思考到使用document: vscode.TextDocument 这样的高级参数呢?

public async provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken)

4)比如返回的这个值,是这样,我看都看不懂,他们是基于什么来进行设计的呢?是在设计的时候就想要一个什么样的形式结果(eg. : Promise<vscode.DocumentSymbol[] | undefined>)再进行设计的是吗?

: Promise<vscode.DocumentSymbol[] | undefined>

共有3个答案

齐昊
2024-08-11

没有写过什么牛��的东西。以我写业务代码的经验来看,优秀的设计来自以下几个方面:
1,重复的思考和迭代。对着一个功能、一段逻辑,思考的多了,自然有成长。
2,模仿他人的写法,学习一些最佳实践
3,悟性

以上要素按重要性排序。

好的设计不是一蹴而就的,一开始可能只是勉强实现功能,在不断的迭代中去优化,慢慢就变成你看到的样子。

景光赫
2024-08-11

这是因为 VSCode 的总负责人兼设计师 Erich Gamma, 是《设计模式:可服用面向对象软件基础》的四人帮之一,目前我们看到的各种设计模式的理论基础都由此书奠定。此外,Erich Gamma 还参与设计了 Eclipse(另一款知名的编辑器)。除负责人外的其它核心成员也有丰富的编辑器开发经验,平均年龄40+。新成员也是开源社区知名插件的开发者。

而对于我们这些没有优秀设计能力的开发者,可以先提高抽象能力,抽象的多了,自然也知道应该如何设计了。

项目刚开始的时候功能比较简单,只有简单几个类。

但是随着项目的进行,功能越来越多,代码越来越复杂,这时候就会将重复的功能抽象出来作为公共基类,让其它类继承它。

项目继续发展,只继承一次也不够,就需要增加中间层,编写各种各样的接口和抽象类。

刚开始的时候传递 number/string 就行了,但是随着项目逐渐复杂,已经不能满足需要,就重构成复杂对象,携带足够多的信息,这样才能满足我们的需要。

大型项目也是从小型项目发展而来的。但是当你有了多个主导大型项目的经验,就可以在开始新项目时大概明白项目的发展方向,提前设计和规划。

堵彬彬
2024-08-11

在面向对象编程中,设计出优秀的项目通常依赖于多方面的因素,包括但不限于设计原则、实践经验、对领域知识的深入理解以及对编程语言和工具的熟练掌握。针对你提到的具体问题和观察,以下是一些可能的解释和建议:

1. 优秀的类设计

  • 使用private readonly字段:这表明开发者对类的封装性和不可变性有深入的理解。private确保字段只能在类内部被访问,减少外部对内部状态的直接操作,有助于维护类的封装性。readonly则确保字段一旦被赋值后就不能被修改,这有助于保持对象状态的一致性,减少因误修改导致的问题。
  • 接口和继承的使用:接口定义了类必须实现的方法,而继承则允许类复用其他类的代码。这些机制使得代码更加模块化和可维护。在大型项目中,接口和继承的使用有助于构建清晰、灵活的系统架构。

2. 泛型的使用

泛型(如CachedResponse<Proto.NavTreeResponse>)允许开发者编写更加灵活和可重用的代码。通过泛型,开发者可以编写与类型无关的代码,然后在需要时指定具体的类型。这有助于减少代码重复,提高代码的可读性和可维护性。

3. 高级参数和返回类型的选择

  • 使用vscode.TextDocument等高级参数:这通常是因为开发者对所使用的库或框架有深入的了解。在Visual Studio Code扩展开发中,vscode.TextDocument是代表文档的标准类型,使用它而不是原始类型(如string)可以提供更多的上下文和功能。
  • 返回类型的设计:返回类型(如Promise<vscode.DocumentSymbol[] | undefined>)的选择反映了开发者对异步编程和错误处理的深入理解。使用Promise可以处理异步操作的结果,而vscode.DocumentSymbol[] | undefined则表明操作可能成功返回符号数组,也可能因为某些原因(如文件不存在)而返回undefined

4. 设计时的考虑

  • 结果形式的设计:在设计函数或方法时,开发者通常会考虑其预期用途和调用者的需求。例如,在provideDocumentSymbols方法中,返回Promise<vscode.DocumentSymbol[] | undefined>可能是因为调用者需要异步地获取文档符号,并且需要处理可能找不到符号的情况。
  • 基于需求进行设计:优秀的软件工程师在设计代码时会优先考虑系统的需求、用户的期望以及代码的可维护性和可扩展性。他们会从整体架构出发,逐步细化到具体的类和函数设计。

建议

  • 深入学习:不断学习和掌握编程语言、框架和工具的新特性和最佳实践。
  • 阅读优秀代码:通过阅读和分析优秀项目的代码,学习其设计思想和实现方式。
  • 实践经验:通过实际的项目开发,积累实践经验,不断反思和改进自己的设计。
  • 参与社区:参与技术社区,与同行交流和学习,获取更多的灵感和建议。
 类似资料:
  • 嘿嘿一个记录贴~ 设计狗的第一份实习→投递简历后一个月收到了HR的面试邀约 面试过程:自我介绍、讲一下作品集中自己觉得最好的作品、面试官提问作品集和学校课程、成绩相关问题、马上下载试用相关app回答问题。 面试问题如下: Q1:介绍一下作品集中最满意的作品吧。 Q2:A项目中你的分工和角色? Q3:界面风格有什么交互和ui方向的考量? Q4:A项目有什么不足?可以改进的地方? Q5:B项目的设计构

  • 状态模式(state pattern)是一个面向对象设计模式。该模式的关键在于一个值有某些内部状态,体现为一系列的 状态对象,同时值的行为随着其内部状态而改变。状态对象共享功能 —— 当然,在 Rust 中使用结构体和 trait 而不是对象和继承。每一个状态对象代表负责其自身的行为和当需要改变为另一个状态时的规则的状态。持有任何一个这种状态对象的值对于不同状态的行为以及何时状态转移毫不知情。 使

  • 面向对象的编程 Scala的博大很大程度上在于它的对象系统。Scala中所有的值都是对象,就这一意义而言Scala是门纯粹的语言;基本类型和组合类型没有区别。Scala也提供了mixin的特性允许更多正交地、细粒度地构造一些在编译时受益于静态类型检测的可被灵活组装的模块。 mixin系统的背后动机之一是消除传统的依赖注入。这种“组件风格(component style)”编程的高潮是是the ca

  • “在面向对象编程中,抽象是对用户隐藏实现细节的过程,只有功能才会提供给用户。” 我一直在试图理解抽象,有人能告诉我们如何准确地隐藏实现细节吗?使用一个程序

  • 组合使用构造函数模式和原型模式 在上述例子中,包含引用类型的原型模式,当修改一个实例的属性值时,其它实例的这个属性值也会相应变化。以及实例要有属于自己的属性这两个问题没有得到解决。 解决以上两个问题的方法:我们可以将  实例本身的属性用 构造函数封装起来,而  将共享的属性和方法用 原型对象封装起来。 创建自定义类型最常见的方法,就是组合使用构造函数模式和原型模式。 构造函数模式用于定义实例自身的

  • 创建对象(设计模式) 虽然Object构造函数或对象字面量都可以用创建对象,但这样有个缺点:使用同一个接口创建许多对象,这样容易产生大量代码。为了减少一个多余的、不必要的代码,我们在创建对象时,可以使用一些常见的设计模式来创建对象。 工厂模式 工厂模式抽象了创建具体对象的过程。用函数来封装 以特定接口 创建对象 的细节。(封装细节--特定接口创建相似对象) 将原始方法封装到函数,并返回这个对象。返

  • 问题内容: 我正在设计一个简单的游戏,该游戏使用Java 2D和牛顿物理学。目前,我的主要“游戏循环”如下所示: 当指示实体更新自身时,它将根据当前施加在其上的力来调整其速度和位置。但是,我需要实体表现出其他行为。例如,如果“坏家伙”被玩家射击,则该实体应被销毁并从游戏世界中移除。 我的问题 :以面向对象的方式实现此目标的最佳方法是什么?到目前为止,我所见过的所有示例都将游戏循环整合到名为的类似的