리팩토링 질문이 있슴다.

요 카테고리가 적절한 지는 잘모르겠네요.
부적절하다면 옮겨주시길… (_ _)

storage 에 데이터를 인덱싱하는 메서드를 만들고 있는데요.
일단 아래와 같은 메서드가 있슴다.

public async Task<(int page, int total)> IndexAsync(int starting, CancellationToken cancellationToken)
{
	var start = starting;
	var totalCount = 0;
	
	var items = await client.GetAsync(starting, cancellationToken);
	if (items.Any() is false)
	{
		return (0, 0);
	}
	
	var contexts = items.OfType<Context>().ToList();
	await client.IndexAsync(contexts, cancellationToken);
	
	totalCount += await client.CountAsync();
	
	return (start, totalCount);
}

public (int page, int total) Index(int starting)
{
	var start = starting;
	var totalCount = 0;
	
	var items = client.Get(starting);
	if (items.Any() is false)
	{
		return (0, 0);
	}
	
	var contexts = items.OfType<Context>().ToList();
	client.Index(contexts);
	
	totalCount += client.Count();
	
	return (start, totalCount);
}

당연히 의사코드입니다. (실행 ㄴㄴ)

데이터를 storage 에 Index 하는 기능을 동기/비동기 모두 지원하기 위해 작성 중인 메서드인데

요거 중간에 client 호출부를 제외한 나머지 로직이 Index / IndexAsync 요 두 메서드 간 완전히 동일합니다.

근데 요걸 중복되는 내용을 정리해서 깔끔하게 리팩토링하고 싶은데
잘 안 되네요…-ㅁ-

혹시 아이디어나 팁이 있으시면 도움 좀 부탁드려요… (_ _)

2 Likes

살짝 코파일럿의 도움을 받아

private (int page, int total) IndexCore(int starting,
Func<int, IEnumerable<object>> getItems,
Action<List<Context>> indexItems,
Func<int> countItems)
{
    var start = starting;
    var totalCount = 0;
    var items = getItems(starting);

    if (items.Any() is false)
    {
        return (0, 0);
    }

    var contexts = items.OfType<Context>().ToList();
    indexItems(contexts);
    totalCount += countItems();

    return (start, totalCount);
}

public async Task<(int page, int total)> IndexAsync(int starting,
CancellationToken cancellationToken)
{
    return await Task.Run(() => IndexCore(starting,
        start => client.GetAsync(start, cancellationToken).Result,
        contexts => client.IndexAsync(contexts, cancellationToken).Wait(),
        () => context.CountAsync().Result), cancellationToken);
}

public (int page, int total) Index(int starting)
{
    return IndexCore(starting,
        start => client.Get(start),
        contexts => client.Index(contexts),
        () => context.Count());
}
1 Like

처음 이거 비슷하게 생각하고 코드를 찌끄려봤는데
비동기 호출 구간에서 Result 나 Wait 을 이용해 블럭을 거는 건 오히려 손해인지라

생각보다 깔끔하게 뭔가 분리는 어려운 거 같네요.

게다가 중간에 삽입되는 비동기 호출 로직이 많아지면, 비동기 호출마다 델리게이트로 인자를 받아야하는 게 약간… 불합리한 느낌적인 느낌?

쉽지 않군요.,… =ㅅ=;;;

비동기 코드는 처음부터 끝까지 비동기로 작성되어야 합니다.

Task는 철학적으로 모나드의 개념을 차용했고 모나드는 코드에 전파되는 형질이 있지요.

요즘은 라이브러리가 비동기 지원이 잘 되어 있어서 동기 코드를 작성하지 말라고 말하는 편이 좋습니다.

2 Likes

닷넷 문서에도 나와 있죠.

일부라도 비동기인 작업은 비동기 작업이라고.

2 Likes

조언 감사합니다.

제가 호출하는 client 쪽에 이미 동기/비동기 모두 지원하고 있어서
저도 동일하게 동기/비동기를 모두 지원하려고 했는데
약간 바보같은 생각이었던 것… =ㅁ=;;;

그냥 비동기 메서드만 작성하고 가죠다 쓰는 쪽에서 알아서 하라고 하는 게 베스트 일 거 같네요. ㅇㅅㅇ/

2 Likes