기능 분리
내가 가장 좋아하는 것 중 하나이며, 내가 그들을 본 이후로 내 마음에 불타오른 두 가지 특정 사항이 있습니다.
Design means break things apart in such a way that they can be put back together.
…그리고
Whenever there is a problem, it's likely because we haven't broken things apart enough.
그 비디오를 본 이후로 저는 코드의 일부를 볼 때마다 이것을 염두에 두었습니다. 그리고 n.2가 예상했던 것보다 훨씬 더 많이 적용된다는 것이 밝혀졌습니다.
이것은 상당히 큰 코드베이스에서 작업하는 동안 최근에 만난 리팩토링 기회에 대한 작은 연습입니다.
node
유형과 어딘가에서 토큰을 가져오는 함수가 있다고 가정하고 특정 경로가 정의된 경우 처리를 계속하기 전에 상위 노드를 가져와야 합니다.type AppNode = {
id: string;
path: string;
};
declare function extractDataFromNode(n: AppNode): string;
async function getNodeData(
startNode: AppNode,
getToken: () => Promise<string>,
getParentNode: (data: AppNode, token: string) => Promise<AppNode>
) {
const token = await getToken();
if (startNode.path === '/p') {
const parent = await getParentNode(startNode, token);
return extractDataFromNode(parent);
}
return extractDataFromNode(startNode);
}
이 기능은 아마 제 역할을 할 것입니다. 하지만 솔직히 말해서, 이것은 제 눈에는 엉망입니다.
n.2에 대한 이유를 묻거나 논쟁할 때 — 내가 일반적으로 받는 응답은 코드를 테스트할 때 모의 함수를 전달할 수 있기 때문에 이러한 "패턴"이 테스트 가능성을 향상시킨다는 것입니다.
describe("returns parent data when path starts with /", () => {
const tokenMock = jest.fn().mockResolvedValue("token");
const parentNodeMock = jest.fn().mockResolvedValue({ id: "9", path: "main" });
const data = getNodeData(
{ id: "0", path: "/s/10" },
tokenMock,
parentNodeMock
);
return expect(data).resolves.toEqual({}); // assertions
});
나는 이것이 유효한 주장이 아니라는 것을 알았다. 나에게 이것은 잘못 결합된 것을 고치는 테이프 패치 방식에 가깝습니다.
Whenever there is a problem, it's likely because we haven't broken things apart enough.
먼저 이 함수에서 토큰 논리를 이동하고 일반 인수로 전달합니다.
async function getNodeData(
startNode: AppNode,
token: string,
getParentNode: (data: AppNode, token: string) => Promise<AppNode>
) {
if (startNode.path === "/p") {
const parent = await getParentNode(startNode, token);
return extractDataFromNode(parent);
}
return extractDataFromNode(startNode);
}
자체적으로 토큰을 가져오는 대신 토큰을 직접 요청하도록 함수 서명을 변경하여 이 함수에서 비순수 연산을 효과적으로 이동했습니다. 좋은 일입니다.
이 단계에서
token
인수가 getParentNode
함수를 제공하기 위해 독점적으로 사용된다는 것을 알 수 있습니다. 이 함수는 인수로 전달됩니다.이에 대해 다음과 같이 할 수 있습니다.
- getParentNode: (data: node, token: string) => Promise<node>
+ getParentNode: (token: string) => (data: AppNode) => Promise<AppNode>
이제 토큰을 외부에서 바인딩하고 이러한 종속성을 제거할 수 있습니다.
async function getNodeData(
startNode: node,
getParentNode: (data: AppNode) => Promise<node>
) {
if (startNode.path === "/p") {
const parent = await getParentNode(startNode);
return extractDataFromNode(parent);
}
return extractDataFromNode(startNode);
}
... 그런 다음 토큰 없는 함수를 호출합니다.
const tokenizedGetParentNode = getParentNode("tokenValue");
getNodeData(
{ id: "test", path: "/p/10" },
tokenizedGetParentNode
);
코드는 이제 틀림없이 더 좋아졌습니다. 여전히, 우리는
getParentNode
를 전달하고 있습니다 — 좋지 않습니다.위에서
getParentNode
함수에 적용한 것과 동일한 솔루션을 반복하여 부모 노드를 미리 전달할 수 있습니다. 하지만 리소스 낭비일 수 있습니다. 상위 노드는 특정 조건에서만 필요합니다.때로는 가독성을 위해 성능/효율성을 희생하는 것이 좋습니다. 그러나 이 사용 사례에서 우리는 부모 노드를 가져오는 것이 우리가 정말로 피하고 싶은 매우 광범위한 작업이라고 가정할 것입니다.
이 함수를 분리하고 현재 노드에서만 작동하도록 범위를 줄여봅시다. 부모 노드가 필요한 경우 이를 실패로 만들고 호출자는 다음을 정렬해야 합니다.
async function getNodeData(startNode: AppNode) {
if (startNode.path === '/p') {
return false;
}
return extractDataFromNode(startNode);
}
그런 다음 호출자는 부모 노드 호출이 필요한지 여부를 이해하기 위해 작업을 수행할 수 있습니다.
우리는 이제 모든 조각이 하나의 작업을 수행하여 성공적으로 분해했습니다. 이제 그것들을 다시 조립할 시간입니다.
declare function getParentNode: (data: AppNode) => RTE.ReaderTaskEither<string, Error, AppNode>;
declare function extractDataFromNode(n: AppNode): string;
function getNodeData(startNode: AppNode) {
if (startNode.path === "/p") {
return E.left(new Error("parent node required"));
}
return E.right(extractDataFromNode(startNode));
}
const getParentNodeData = (startNode: AppNode) =>
pipe(getParentNode(startNode), RTE.map(extractDataFromNode))(token);
const node: AppNode = { id: "10", path: "/path" };
return pipe(
TE.fromEither(getNodeData(node)),
TE.orElse(() => getParentNodeData(node))
);
이제 가서 기능을 분리하십시오.
그런 다음 시스템을 분해하십시오.
그런 다음 다시 함께 넣습니다.
Reference
이 문제에 관하여(기능 분리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/vncz/break-functions-apart-162l텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)