【问题标题】:How to implement Dependency Injection and Single Responsibility Principle in the code如何在代码中实现依赖注入和单一职责原则
【发布时间】:2019-06-28 02:35:03
【问题描述】:

我正在创建一个新的简单应用来标记和删除给定文本中的停用词。为此,我创建了以下文件:

// Stopword.js
class StopWord {
    constructor (stopWords) {
        this.stopWords = stopWords
    }
    remove (value) {
        // do whatever we need to remove this.stopWords from the passed value and return it
    }

}
// PreProcess.js
const StopWord = require('./StopWord')
class PreProcess {
    setValue (value) {
        this.value = value
        return this
    }
    removeStopWords (stopWords) {
        const stopWord = new StopWord(stopWords)
        return stopWord.remove(this.value)
    }

}
// Indexed.js
class Indexer {
    setValue (value) {
        this.value = value
        return this
    }
    setStopWords (stopWords) {
        this.stopWords = stopWords
        return this
    }
    index () {
        return this.preprocess
            .setValue(this.value)
            .removeStopWords(stopWords)
    }

}
// main.js
const indexer = new Indexer()
const result = indexer
    .setValue('a sample text ')
    .setStopWords(['a', 'an'])
    .index()

console.log(result)

假设,在某些情况下,我们想从数据库中动态加载停用词(不同的用户使用不同的停用词)。出现的第一个问题是我们需要在哪个类中从数据库中加载停用词? 很明显,PreProcess 类是使用依赖注入注入索引器的。 StopWord 类也可以使用 DI 注入,但我想知道这是否足够好。第二个问题是应该将哪些类注入到哪些?

【问题讨论】:

    标签: javascript dependency-injection architecture software-design single-responsibility-principle


    【解决方案1】:

    不要试图根据需要使事情变得更复杂 :-) 根据您目前拥有的名称,我不知道如何使用该功能。什么是索引器?什么是预处理器?这些东西可以是任何东西。因此,请保持简单,从领域的角度直截了当(我将在 TypeScript 中为您提供示例,以便您了解在哪里使用了哪些类型):

    首先我们将定义停用词领域服务(我们的业务领域):

    /** we'll use this to provide stop words as array of strings */
    class StopWords {
        public stopWords: string[] = [];
        constructor(words?: string[]) { this.stopWords = words }
    }
    /** single responsibility of this class: actually remove the stopwords */
    class StopWordsRemovalService {
        /** inject stopwords through constructor */
        constructor(stopWords: StopWords) {}
        /** here we do the real removal work */
        public removeAllStopWordsFrom(text: string): string { return ''; }
    }
    

    现在我们可以从任何位置提供停用词(更多在基础设施方面):

    /** TypeScript provids interfaces, which you don't have in plain JS. It merely 
     * defines the class method signatures, which is quite useful in software design */
    interface StopWordsProvider {
        getStopWords(): StopWords;
    }
    class DefaultStopWordsProvider implements StopWordsProvider {
        getStopWords(): StopWords {
            return new StopWords(['a', 'an']);
        }
    }
    class DbStopWordsProvider implements StopWordsProvider {
        getStopWords(): StopWords {
            return db.query("SELECT stopWords FROM ...");
        }
    }
    

    最后我们将连接在一起:

    const swremoval: StopWordsRemovalService  = new StopWordsRemovalService(new DefaultStopWordsProvider().getStopWords());
    swremoval.removeAllStopWordsFrom('a sample text');
    

    要将事物连接在一起,您现在可以使用依赖注入框架,例如 InversifyJS


    更新:我们需要按用户 ID 返回不同的停用词。

    我想到的第一个问题是用户 ID 对企业有多重要?如果总是需要用户 ID 来确定停用词,则用户 ID 是我们域中不可或缺的一部分!如果有时需要用户 ID 来确定停用词,它可能不是我们域的组成部分。让我们来看看这两种情况:

    总是需要用户 ID 才能检索停用词

    如果用户 ID 对域很重要并且总是需要确定停用词,那么让我们将其作为合同的一部分:

    /** TypeScript provids interfaces, which you don't have in plain JS. It merely 
     * defines the class method signatures, which is quite useful in software design */
    interface StopWordsProvider {
        /** Returns all the stop words for the given user */
        getStopWords(userID: number): StopWords;
    }
    

    现在所有实现此接口的类都需要尊重用户 ID。

    有时需要用户 ID 才能检索停用词

    如果用户 ID 仅用于某些查找,我们不会更改停用词合同(即界面)!相反,我们将提供一个 UserSpecificStopWordsProvider 来进行查找。要配置用户 ID,我们将使用工厂模式:

    /** The user-specific stopwords provider is configured through
     *  constructor-based injection */
    class UserSpecificStopWordsProvider implements StopWordsProvider {
        constructor(private userId: number) {}
        getStopWords(): StopWords {
            return db.query("SELECT * FROM sw WHERE userId = :userId", this.userId);
        }
    }
    
    /** To simplify the usage, we'll provide a factory to do this. */
    class UserSpecificStopWordsProviderFactory {
        factoryProvider(userId: number): StopWordsProvider {
            // potentially inject more dependencies here
            return new UserSpecificStopWordsProvider(userId);
        }
    }
    
    /** We can then use it as follows: */
    const factory = new UserSpecificStopWordsProviderFactory();
    factory.factoryProvider(5).getStopWords();
    

    【讨论】:

    • 现在假设StopWordsProvider本身依赖于user_id,它必须为不同的user_id返回不同的数组。在这种情况下,结构如何更好?
    • 我刚刚添加了关于如何将基于用户 ID 的停用词提供程序添加到我的答案的说明。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 2010-11-26
    • 2016-07-31
    相关资源
    最近更新 更多