https://www.linkedin.com/pulse/types-coupling-ahmed-adel/
들어가기 앞서
- 스프링은 거대한 팩토리 메서드에 불과하다.
- 객체 생성과 종속성 주입을 통한 객체 관계 그래프 생성을 대신해서 거대한 결합도를 대신 처리해준다.
- new 키워드는 강결합이다.
- 스프링이 없는 프론트엔드는 객체 생성과 종속성 주입을 개발자가 직접 구현해야 한다
- contextAPI의 컨텍스트 / 컨테이너 패턴의 컨테이너
- 여기서 수 많은 잡다한 의존성을 처리한다.
- 해당 패턴은 결합도 처리의 핵심이다.
- 프리젠테이션 컴포넌트, 혹은 useContext 훅을 사용하는 컨슈머 컴포넌트는 컨텍스트 / 컨테이너의 존재에 상관없이 인터페이스 기반으로 api(프롭, 콜백함수, 훅)를 컨슘한다.
- contextAPI의 컨텍스트 / 컨테이너 패턴의 컨테이너
1- Content Coupling
이 결합은 한 클래스가 다른 클래스의 내부 상태(예: 변수)를 수정할 때 발생합니다.
이 결합에 대한 해결책은 내부 상태 멤버를 "프라이빗"으로 선언하는 것이며
퍼블릭 메서드를 통하지 않고는 업데이트되지 않아야 합니다.
프라이빗 변수에 "getters" 및 "setters"를 제공할 수 있지만(이 자체는 나쁘지 않음),
이 변수에 대한 계산이 다른 클래스에서 수행는 경우 콘텐츠 결합을 방지하지 못할 수 있습니다.
예를 들면 다음과 같습니다.
// tight coupling :
public int sumValues(Calculator c){
int result = c.getFirstNumber() + c.getSecondNumber();
c.setResult(result);
return c.getResult();
}
// loose coupling :
public int sumValues(Calculator c){
c.sumAndUpdateResult();
return c.getResult();
}
- new 생성자 호출 / 객체 리터럴 생성도 위의 결합도를 생성합니다.
- 팩터리 메서드를 통해 강결합을 한 곳으로 몰아야합니다.
- 이걸 해주는게 스프링입니다.
2- Common Coupling
- 공유 데이터 관리 클래스를 만들어, 해당 데이터 변경에 대한 책임을 자신이 지게 합니다.
- 단순 조회만 하는것은 문제가 안됩니다.
Android 애플리케이션의 또 다른 솔루션은 리소스 XML 파일을 사용하고 "R" 클래스를 통해 해당 상수에 액세스하는 것입니다.
이 클래스는 프로젝트에서 제거되거나 이름이 바뀌지 않기 때문입니다.
- 대체적으로 커먼 커플링 까지를 매우 높은 결합도로 취급하는것 같습니다.
- 해당 커플링을 싱글턴, 종속성 주입을 통해 추상화 하는것이 팩터리 패턴입니다.
여긴 설명만으로 이해가 잘 안되는것 같아 자바스크립트 코드를 첨부합니다.
var Global = "global";
function A() {
Global = "A";
}
function B() {
Global = "B";
}
3- Control Coupling
이 커플링은 한 함수가 "매개변수"를 사용하여 다른 함수를 호출하고
이 "매개변수"가 호출된 함수가 특정 작업을 트리거하도록 강제할 때 발생합니다.
이로 인해 두 번째 함수가 첫 번째 함수에 의해 제어됩니다.
switch-case 및 if-else 블록이 발생하는 곳에서 찾을 수 있습니다.
이러한 결합의 결과는 호출자와 호출된 함수 모두에 영향을 주는 변경을 야기할 것입니다.
컨트롤 결합을 제거하기 위해 다형성 기술을 사용할 수 있습니다.
// tight coupling :
public void run(){
takeAction(1);
}
public void takeAction(int key) {
switch (key) {
case 1:
System.out.println("ONE RECEIVED");
break;
case 2:
System.out.println("TWO RECEIVED");
break;
}
}
// loose coupling :
public void run(){
Printable printable = new PrinterOne();
takeAction(printable);
}
public void takeAction(Printable printable){
printable.print();
}
public interface Printable{
void print();
}
public class PrinterOne implements Printable{
@Override
public void print() {
System.out.println("ONE RECEIVED");
}
}
public class PrinterTwo implements Printable{
@Override
public void print() {
System.out.println("TWO RECEIVED");
}
}
(주 : 어차피 팩터리에서 플래그 받아서 처리해야 할텐데... 이런 식으로)
var TableClothFactory = {
getTableCloth: function(color) {
return Object.create(TableCloth, { color: { value: color } });
}
};
js는 map혹은 객체 리터럴 활용
public static void main(String ... args){
new Printer().takeAction(1);
}
@CommandsMapFactory
public class Printer {
private final CommandsMap map = CommandsMap.of(this);
public void takeAction(int key){
map.execute(key);
}
@Command(1)
void printOne() {
System.out.println("ONE RECEIVED");
}
@Command(2)
void printTwo() {
System.out.println("TWO RECEIVED");
}
}
4- Stamp Coupling
public class NumberGenerator {
public Integer generate() {
return (int) (Math.random() * 100);
}
}
public class Printer {
// stamp coupling - happens when using another class as the type
// of the method parameter
public void printNumber(NumberGenerator generator) {
System.out.println(generator.generate());
}
}
public class PrinterWrapper {
private final Printer printer;
public PrinterWrapper(Printer printer) {
this.printer = printer;
}
// we must depende on NumberGenerator.java although this class
// is concerned with Printer.java Wrapping ... this class is now
// coupled with NumberGenerator.java due to the method
// parameter type in Printer.java
public void print(NumberGenerator generator) {
printer.printNumber(generator);
}
}
public class Printer {
public void printNumber(int generatedNumber) {
System.out.println(generatedNumber);
}
}
public class EmailSender {
public void send(SendParams params) {
// ...
}
// encapsulation in such kind of Objects is not neccessary
// since they are not actual objects, they are a data structure
// that is used only once before passing it to the send() method
public static class SendParams {
public String email;
public String title;
public String signature;
public String content;
}
}
또 다른 방법은 인터페이스를 사용하는 것입니다. 여기서 인터페이스는 구체적인 구현이 아니라 추상적인 계약입니다.
(콜백과 유사한 개념)
public class NumberGenerator implements Callable<Integer>{
public Integer call() {
return (int) (Math.random() * 100);
}
}
public class Printer {
public void printNumber(Callable<Integer> numberGenerator) {
System.out.println(numberGenerator.call());
}
}
public class PrinterWrapper {
private final Printer printer;
public PrinterWrapper(Printer printer) {
this.printer = printer;
}
public void print(Callable<Integer> numberGenerator) {
printer.printNumber(numberGenerator);
}
}
5- Data Coupling
(밥 삼촌의 책 "Clean Code"에 따르면 세 개의 매개변수는 많은 것으로 간주됨)
6- Routine Call Coupling
이 결합은 루틴/메서드/함수가 다른 것을 호출할 때 발생하며
이 결합은 모든 시스템에 존재합니다.
7- Type Use Coupling
public class NumberGenerator implements Callable<Integer> {
public Integer call() {
return (int) (Math.random() * 100);
}
}
// tight coupling :
public class Printer {
// depends on a specific implementation
private NumberGenerator generator;
public Printer(NumberGenerator generator) {
this.generator = generator;
}
public void printNumber() {
System.out.println(generator.call());
}
}
// loose coupling :
public class Printer {
// depends on an interface
private Callable<Integer> generator;
public Printer(Callable<Integer> generator) {
this.generator = generator;
}
public void printNumber() {
System.out.println(generator.call());
}
}
- 구체적인것이 추상적인 것에 의존하도록 하라
- 구체 클래스를 추상적인 인터페이스를 구현하도록 하여, 사용하는 곳에서 추상 인터페이스만 사용할 수 있게 함
8- Import Coupling
가져온 패키지가 변경된다고 가정해봅시다.
변경된 패키지가 기존의 다른 import된 클래스와 충돌하는 이름으로 클래스를 선언하여
기존 클래스에 정규화된 이름을 사용하는 것과 같이 import된 클래스도 강제로 변경될 수 있습니다.
9- External Coupling
- 이것도 써먹으려면 팩토리 필요함
더 볼만한 자료
https://ui.toast.com/weekly-pick/ko_20150522
new, 객체 리터럴 생성은 강결합.
해당 객체 사용을 (추상) 팩터리 메서드로 대체
'FrontEnd' 카테고리의 다른 글
[3분 리액트] React18의 useInsertionEffect 훅에 대해 알아보자 (0) | 2022.08.03 |
---|---|
CSS 애니메이션 성능 분석 및 개선하기 (0) | 2022.08.02 |
React 18의 useSyncExternalStore, Tearing 현상은 무엇인가? (0) | 2022.08.01 |
리액트의 의존성 주입 with Context API (dependency injection) (0) | 2022.08.01 |
리액트 프로젝트의 결합도를 관리하는 방법 (3) | 2022.07.31 |