【问题标题】:Javascript - How to define a class in multiple modules?Javascript - 如何在多个模块中定义一个类?
【发布时间】:2021-03-30 06:45:01
【问题描述】:

我有一堂课,它变得非常长。我已经确定了多个不同的概念,它们共同为我的课程赋予了意义。

MyService”类由:“auth”、“storage”、“database”组成, ... 概念。

我认为(也许我错了)在多个文件中定义类“MyService”将是一个很好的重构。比如:

MyService/MyService.js 从“./myService”导入我的服务;

export default function MyService() {
     if(!isInitialized) {
         myService.initializeApp(config);

         // Only after doing myService.initializeApp(config);
         this.auth = myService.auth();
         this.storage = myService.storage();
         this.database = myService.database();

         isInitialized = true;
     }
}

MyService/Auth.js

Add auth methods to MyService class prototype

MyService/Storage.js

Add storage methods to MyService class prototype

...

我该怎么做?这种技术有名字/存在吗?这是重构我的课程的好方法吗?如果是,为什么?如果不是,那为什么?

谢谢。

更新

也许,将类拆分为更小的类是最好的主意,但我不知道如何使它像访问 myService 功能一样工作, 这一行

myService.initializeApp(config);

之前必须在单例中执行过。

基于解决方案的示例

/* Simulate myService CLI SDK */
function myService() {}

myService.key = undefined;

myService.initializeApp = function (key) {
  this.key = key;
} 

myService.auth = function () {
  if(!this.key) {
    throw new Error("Please, initialize app");
  }

  console.log(`You have access to all auth methods! Key: ${this.key}`);
}

myService.storage = function () {
  if(!this.key) {
    throw new Error("Please, initialize app")
  }

  console.log(`You have access to all storage methods! Key: ${this.key}`);
}



/* Classes that compose the main class */

function Auth() {
  this.auth = myService.auth();
}

Auth.prototype.login = function (username, password) {
  console.log("Login!");
}

function Storage() {
  this.storage = myService.storage();
}

Storage.prototype.uploadFile = function (file, path) {
  console.log("Uploading file!")
}




/* Main class */

let isInitialized = false;
function Firebase() {
  if(!isInitialized) { // Singleton
    myService.initializeApp("raul");
    this.auth = new Auth(); // Composition
    this.storage = new Storage(); // Composition
    isInitialized = true;
  }
}

const f = new Firebase();
f.auth.login();
f.storage.uploadFile();

【问题讨论】:

    标签: javascript oop refactoring ecmascript-5


    【解决方案1】:

    与其将MyService 分散到多个模块中,不如将其分解为更小的类。你说过:

    “MyService”类由:“auth”、“storage”、“database”、...概念组成。

    因此,有一个用于身份验证的类,另一个用于存储,另一个用于数据库,然后让 MyService 使用这些其他类的实例作为其实现的一部分可能是有意义的。

    您不能跨模块传播 class 定义。(您的编辑表明您出于某种原因没有使用 class 语法。)您可以 事后将近方法添加到类中,如下所示:

    class Example {
        // ...
    }
    

    然后在另一个模块中:

    import Example from "./Example.js";
    
    // (You'd probably have a utility method for this, `addMethod` or similar)
    Object.defineProperty(Example.prototype, "methodName", {
        value() {
            // ...method implementation...
        },
        enumerable: false, // (You can leave this off, false is the default)
        configurable: true,
        writable: true,
    });
    

    但是像这样散布定义可能不是最佳实践,并且存在限制,尤其是在子类中(super 在以这种方式添加的方法中不会像 superclass 定义会)。 (您的编辑表明您没有使用 class 语法,因此与您无关。)

    所以我会坚持将MyService 分解成可以组合的更小的类。


    您可能想知道为什么我在上面使用了Object.defineProperty,而不仅仅是:

    Example.prototype.methodName = function() {
        // ...
    };
    

    区别在于可枚举性。如果你使用这样的赋值添加一个方法,它会创建一个可枚举的属性,但如果你使用Object.defineProperty,如上所示,它不会。我对enumerableconfigurablewritable 使用与class 定义在创建方法时使用的相同标志。这是一个不同的例子:

    class A { }
    Object.defineProperty(A.prototype, "example", {
        value() {},
        enumerable: false,
        configurable: true,
        writable: true,
    });
    
    class B { }
    B.prototype.example = function() { };
    
    const a = new A();
    console.log(`typeof a.example = ${typeof a.example}`);
    console.log(`a's enumerable non-Symbol properties:`);
    let count = 0;
    for (const name in a) {
        console.log(`* ${name}`);
        ++count;
    }
    console.log(`Total: ${count}`);
    
    const b = new B();
    console.log(`typeof b.example = ${typeof b.example}`);
    console.log(`b's enumerable non-Symbol properties:`);
    count = 0;
    for (const name in b) {
        console.log(`* ${name}`);
        ++count;
    }
    console.log(`Total: ${count}`);
    .as-console-wrapper {
        max-height: 100% !important;
    }

    请注意,a 没有任何可枚举的属性,但 b 有——因为它继承了一个

    【讨论】:

    • 我想这样做,但在我的用例中,主要问题是访问 this.auth、this.storage 或 this.database,我必须确保 myService.initializeApp( config) 已在我的构造函数的单例中执行。
    • @Raul - 如果MyService 是较小类的组合,我不明白为什么在构造函数中创建其他类的实例后它不能这样做。
    • 非常感谢!有用!我已经根据您描述的解决方案使用示例编辑了问题,您可以检查它是否与您描述的完全一致?另外,为了简单起见,我使用原型,但我会按照你说的方式进行!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    相关资源
    最近更新 更多