如何在代码中实现依赖注入和单一职责原则

How to implement Dependency Injection and Single Responsibility Principle in the code

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

// 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)

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

不要试图根据需要使事情变得更复杂 :-) 从您目前拥有的名称来看,我不知道如何使用该功能。什么是索引器?什么是预处理器?这些东西可以是任何东西。因此,保持简单,从领域的角度直截了当(我会给你 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


更新:我们需要 return 用户 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();