diff --git a/src/features/commands/internal/VoiceChannel/internal/VoiceChannel.class.ts b/src/features/commands/internal/VoiceChannel/internal/VoiceChannel.class.ts index b36d5f33..9dcd6756 100644 --- a/src/features/commands/internal/VoiceChannel/internal/VoiceChannel.class.ts +++ b/src/features/commands/internal/VoiceChannel/internal/VoiceChannel.class.ts @@ -1,5 +1,6 @@ import { Voice } from '@/features/core/index.js'; import { LogicException } from '@/features/others/Error/LogicException.js'; +import { singleton } from '@/features/others/singleton/index.js'; import { VoiceChannelCommandOptions } from './VoiceChannel.constants.js'; import type { InteractArgs } from '../../CommandBase/index.js'; @@ -12,7 +13,9 @@ export class VoiceChannel extends CommandBase { public override async interact({ interaction }: InteractArgs): Promise { const subcommand = interaction.options.getSubcommand(); - const voice = Voice.getInstance(); + const createVoiceInstance = () => new Voice(); + const getVoiceInstance = singleton(createVoiceInstance); + const voice = getVoiceInstance(); switch (subcommand) { case VoiceChannelCommandOptions.join.name: diff --git a/src/features/core/internal/Voice/internal/Voice.class.ts b/src/features/core/internal/Voice/internal/Voice.class.ts index d28c81e9..b058262f 100644 --- a/src/features/core/internal/Voice/internal/Voice.class.ts +++ b/src/features/core/internal/Voice/internal/Voice.class.ts @@ -11,25 +11,11 @@ import { } from '@/features/library/index.js'; import { getInteractionMemberId } from '@/features/others/discord/index.js'; -/** - * Singleton class for voice. - * NOTE: シングルトンの影響で複数サーバにしたときのテストしないと困るかも - */ export class Voice { - static #instance: Voice | null = null; #connection: Array<{ guildId: string; connection: VoiceConnection; }> = []; - private constructor() {} - - public static getInstance(): Voice { - if (Voice.#instance === null) { - Voice.#instance = new Voice(); - } - - return Voice.#instance; - } public async join({ interaction, diff --git a/src/features/others/singleton/index.ts b/src/features/others/singleton/index.ts new file mode 100644 index 00000000..9e99b96f --- /dev/null +++ b/src/features/others/singleton/index.ts @@ -0,0 +1 @@ +export { singleton } from './internal/singleton.func.js'; diff --git a/src/features/others/singleton/internal/singleton.func.ts b/src/features/others/singleton/internal/singleton.func.ts new file mode 100644 index 00000000..ff7774fc --- /dev/null +++ b/src/features/others/singleton/internal/singleton.func.ts @@ -0,0 +1,9 @@ +type Getter = () => T; +type Factory = () => T; + +export const singleton = (factory: Factory): Getter => + ((): Getter => { + let memo: T | null = null; + + return () => (memo ? memo : (memo = factory())); + })(); diff --git a/src/features/others/singleton/internal/singleton.spec.ts b/src/features/others/singleton/internal/singleton.spec.ts new file mode 100644 index 00000000..3d28911e --- /dev/null +++ b/src/features/others/singleton/internal/singleton.spec.ts @@ -0,0 +1,19 @@ +import { singleton } from './singleton.func.js'; + +it('not identical', () => { + const factory = () => ({}); + + const x = factory(); + const y = factory(); + + expect(x).not.toBe(y); +}); +it('with singleton, identical', () => { + const factory = () => ({}); + const getter = singleton(factory); + + const x = getter(); + const y = getter(); + + expect(x).toBe(y); +});