-
결합도(Coupling)와 응집도(Cohesion) 개념과 순서 및 자바 코드전산학/소프트웨어공학 2024. 7. 30. 21:39728x90반응형
맨날 헷갈리는 결합도(Coupling)과 응집도(Cohesion) 개념과 순서를 정리해본다.
결합도(Coupling)
결합도 >>> 낮을 수록 좋음
(강) 내용 - 공통 - 외부 - 제어 - 스탬프 - 자료 - 메시지 - 기능 (약)
■내용 결합도 -> 내용을 직접 참조 하거나 수정하는 경우, 유지보수와 확장이 매우 어려움
내용 결합도 줄이기, 접근 제어자 활용하여 캡슐화 강화// ModuleB 클래스 정의 class ModuleB { public int data; public ModuleB(int data) { this.data = data; } public void display() { System.out.println("Data: " + data); } } // ModuleA 클래스 정의 class ModuleA { private ModuleB moduleB; public ModuleA(ModuleB moduleB) { this.moduleB = moduleB; } // ModuleB의 내부 데이터를 직접 수정 public void modifyModuleBData(int newData) { moduleB.data = newData; // ModuleB의 내부 변수를 직접 수정 } } // 메인 클래스 public class Main { public static void main(String[] args) { ModuleB moduleB = new ModuleB(10); ModuleA moduleA = new ModuleA(moduleB); moduleB.display(); // Output: Data: 10 // ModuleA가 ModuleB의 내부 데이터를 직접 수정 moduleA.modifyModuleBData(20); moduleB.display(); // Output: Data: 20 } }
■공통 결합도 -> 여러 모듈이 공통 데이터를 공유하는 경우, 일반적으로 전역 변수를 통해 이루어지며
강한 의존성 초래, 디버깅이 어렵고 오류가 발생할 가능성이 높다
-> 캡슐화를 통해 공통 결합도를 낮춘다// 공통 데이터를 정의한 클래스 class SharedData { public static int sharedValue = 0; } // ModuleA 클래스 정의 class ModuleA { public void incrementSharedValue() { SharedData.sharedValue++; System.out.println("ModuleA incremented shared value: " + SharedData.sharedValue); } } // ModuleB 클래스 정의 class ModuleB { public void decrementSharedValue() { SharedData.sharedValue--; System.out.println("ModuleB decremented shared value: " + SharedData.sharedValue); } } // 메인 클래스 public class Main { public static void main(String[] args) { ModuleA moduleA = new ModuleA(); ModuleB moduleB = new ModuleB(); moduleA.incrementSharedValue(); // Output: ModuleA incremented shared value: 1 moduleB.decrementSharedValue(); // Output: ModuleB decremented shared value: 0 // SharedData.sharedValue is shared between ModuleA and ModuleB System.out.println("Final shared value: " + SharedData.sharedValue); // Output: Final shared value: 0 } }
■외부 결합도 -> 모듈이 외부 인터페이스를 통해서만 상호 작용 하는 경우
외부 인터페이스는 파일, 디바이스, 통신 프로토콜 등을 포함할 수 있다.
이는 시스템 외부의 요소에 의존성을 가지며, 외부 시스템의 변경이 모듈에 영향을 미칠 수 있다.
- 외부 시스템 의존성
-외부 시스템의 변경이 모듈에 영향
-유지보수성(캡슐화 되어 있어야 유지 보수 용이)import java.io.*; // FileManager 클래스는 파일 읽기 및 쓰기 기능을 제공 class FileManager { private String filePath; public FileManager(String filePath) { this.filePath = filePath; } public void writeToFile(String data) throws IOException { BufferedWriter writer = new BufferedWriter(new FileWriter(filePath)); writer.write(data); writer.close(); } public String readFromFile() throws IOException { BufferedReader reader = new BufferedReader(new FileReader(filePath)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } reader.close(); return sb.toString(); } } // ModuleA 클래스 정의 class ModuleA { private FileManager fileManager; public ModuleA(FileManager fileManager) { this.fileManager = fileManager; } public void saveData(String data) { try { fileManager.writeToFile(data); System.out.println("ModuleA saved data to file."); } catch (IOException e) { System.err.println("Error saving data: " + e.getMessage()); } } } // ModuleB 클래스 정의 class ModuleB { private FileManager fileManager; public ModuleB(FileManager fileManager) { this.fileManager = fileManager; } public void loadData() { try { String data = fileManager.readFromFile(); System.out.println("ModuleB loaded data: " + data); } catch (IOException e) { System.err.println("Error loading data: " + e.getMessage()); } } } // 메인 클래스 public class Main { public static void main(String[] args) { String filePath = "data.txt"; FileManager fileManager = new FileManager(filePath); ModuleA moduleA = new ModuleA(fileManager); ModuleB moduleB = new ModuleB(fileManager); // ModuleA가 데이터를 파일에 저장 moduleA.saveData("Hello, world!"); // ModuleB가 파일에서 데이터를 읽어옴 moduleB.loadData(); } }
■제어 결합도 -> 한 모듈이 다른 모듈의 제어 흐름을 변경하기 위해 제어 정보를 전달하는 경우
한 모듈이 다른 모듈의 동작을 결정하는 플래그나 인수를 전달, 그 모듈의 내부 로직이나 흐름을 변경
모듈 간의 독립성을 저하 시킬 수 있으며, 유지보수성을 낮추는 원인이 될 수 있다.
제어 결합도 줄이기 -> 다형성 사용, 콜백 메서드 사용// ModuleB 클래스 정의 class ModuleB { public void performAction(int controlFlag) { if (controlFlag == 1) { System.out.println("Action 1 performed by ModuleB."); } else if (controlFlag == 2) { System.out.println("Action 2 performed by ModuleB."); } else { System.out.println("Default action performed by ModuleB."); } } } // ModuleA 클래스 정의 class ModuleA { private ModuleB moduleB; public ModuleA(ModuleB moduleB) { this.moduleB = moduleB; } public void execute(int controlFlag) { // ModuleA가 controlFlag를 통해 ModuleB의 동작을 제어 moduleB.performAction(controlFlag); } } // 메인 클래스 public class Main { public static void main(String[] args) { ModuleB moduleB = new ModuleB(); ModuleA moduleA = new ModuleA(moduleB); // ModuleA가 controlFlag를 전달하여 ModuleB의 동작을 제어 moduleA.execute(1); // Output: Action 1 performed by ModuleB. moduleA.execute(2); // Output: Action 2 performed by ModuleB. moduleA.execute(3); // Output: Default action performed by ModuleB. } }
■스탬프 결합도 -> 모듈 간에 데이터 구조가 전달되는 경우, 데이터 구조 전체를 전달 받아 사용하는 경우
필요한 데이터만 전달하는 것이 아니라 데이터 구조 전체를 받아서 사용하는 구조
스탬프 결합도 줄이기 위해선 필요한 데이터만 전달받도록 변경, 의존성 줄이고 데이터 구조 변경의 영향을 최소화 할 수 있음// 데이터 구조 정의 class Data { public int id; public String name; public String description; public Data(int id, String name, String description) { this.id = id; this.name = name; this.description = description; } } // ModuleB 클래스 정의 class ModuleB { public void processData(Data data) { System.out.println("Processing Data:"); System.out.println("ID: " + data.id); System.out.println("Name: " + data.name); System.out.println("Description: " + data.description); } } // ModuleA 클래스 정의 class ModuleA { private ModuleB moduleB; public ModuleA(ModuleB moduleB) { this.moduleB = moduleB; } public void sendData() { Data data = new Data(1, "Sample Name", "Sample Description"); moduleB.processData(data); } } // 메인 클래스 public class Main { public static void main(String[] args) { ModuleB moduleB = new ModuleB(); ModuleA moduleA = new ModuleA(moduleB); // ModuleA가 Data 객체를 ModuleB에 전달 moduleA.sendData(); } }
■자료 결합도 -> 필요한 데이터를 전달하는 경우, 불필요한 세부 정보는 공유하지 않음, 유지 보수성과 재사용성을 높이는데 도움이 된다, 가장 낮은 결합도
독립성, 유지보수성, 재사용성, 명확성// ModuleB 클래스 정의 class ModuleB { public void process(int id, String name) { System.out.println("Processing Data:"); System.out.println("ID: " + id); System.out.println("Name: " + name); } } // ModuleA 클래스 정의 class ModuleA { private ModuleB moduleB; public ModuleA(ModuleB moduleB) { this.moduleB = moduleB; } public void sendData() { int id = 1; String name = "Sample Name"; moduleB.process(id, name); } } // 메인 클래스 public class Main { public static void main(String[] args) { ModuleB moduleB = new ModuleB(); ModuleA moduleA = new ModuleA(moduleB); // ModuleA가 필요한 데이터만 ModuleB에 전달 moduleA.sendData(); } }
응집도 >>> 높을 수록 좋음
(낮) 우연 - 논리 - 시간 - 절차 - 통신 - 순차 - 기능 (높)
■우연적 응집도 -> 가장 낮은 수준의 응집도, 모듈 내의 요소들이 아무런 관련없이 우연히 한 모듈에 포함된 경우
서로 다른 기능을 수행, 모듈 내의 요소들이 단순히 물리적으로 같은 위치에 있어서 모여있는 것
모듈이 단일 책임을 가지도록 해야 함, 각 기능을 별도의 클래스에 분리할 수 있음class Utility { public void printMessage() { System.out.println("Printing a message..."); } public void calculateSum(int a, int b) { System.out.println("Sum: " + (a + b)); } public void convertToUpperCase(String input) { System.out.println("Uppercase: " + input.toUpperCase()); } public void generateRandomNumber() { int random = (int) (Math.random() * 100); System.out.println("Random number: " + random); } public void reverseString(String input) { StringBuilder reversed = new StringBuilder(input).reverse(); System.out.println("Reversed: " + reversed); } } // 메인 클래스 public class Main { public static void main(String[] args) { Utility utility = new Utility(); // 여러 가지 무관한 기능들을 호출 utility.printMessage(); utility.calculateSum(5, 10); utility.convertToUpperCase("hello"); utility.generateRandomNumber(); utility.reverseString("hello"); } }
■논리적 응집도 -> 한 모듈이 논리적으로 유사한 기능들을 수행하는 경우, 유사한 종류의 작업을 수행하지만 실제로는 서로 다른 작업을 처리class Utility { public void printMessage() { System.out.println("Printing a message..."); } public void calculateSum(int a, int b) { System.out.println("Sum: " + (a + b)); } public void convertToUpperCase(String input) { System.out.println("Uppercase: " + input.toUpperCase()); } public void generateRandomNumber() { int random = (int) (Math.random() * 100); System.out.println("Random number: " + random); } public void reverseString(String input) { StringBuilder reversed = new StringBuilder(input).reverse(); System.out.println("Reversed: " + reversed); } } // 메인 클래스 public class Main { public static void main(String[] args) { Utility utility = new Utility(); // 여러 가지 무관한 기능들을 호출 utility.printMessage(); utility.calculateSum(5, 10); utility.convertToUpperCase("hello"); utility.generateRandomNumber(); utility.reverseString("hello"); } }
■시간적 응집도 -> 특정 시점에서 수행되는 기능들이 하나의 모듈에 포함되는 경우, 프로그램 초기화 시점에서 실행되는 여러 기능들이 하나의 모듈에 포함되거나, 프로그램 종료 시점에서 실행되는 여러 기능들이 하나의 모듈에 포함되는 경우class Initialization { public void initializeLogging() { System.out.println("Initializing logging..."); // 로그 시스템 초기화 코드 } public void loadConfiguration() { System.out.println("Loading configuration..."); // 설정 파일 로드 코드 } public void initializeDatabase() { System.out.println("Initializing database..."); // 데이터베이스 연결 초기화 코드 } public void initializeCache() { System.out.println("Initializing cache..."); // 캐시 시스템 초기화 코드 } } // 메인 클래스 public class Main { public static void main(String[] args) { Initialization initialization = new Initialization(); // 프로그램 초기화 시점에서 실행되는 기능들 호출 initialization.initializeLogging(); initialization.loadConfiguration(); initialization.initializeDatabase(); initialization.initializeCache(); System.out.println("Initialization complete. Program is starting..."); } }
■절차적 응집도 -> 모듈 내 구성 요소들이 특정 순서에 따라 수행되는 경우, 각 단계가 특정한 순서대로 수행되도록 모듈에 포함된 경우를 의미
모듈 내 기능들이 순차적으로 관련이 있음class FileProcessor { public void openFile(String fileName) { System.out.println("Opening file: " + fileName); // 파일을 여는 코드 } public void readFile() { System.out.println("Reading file contents..."); // 파일 내용을 읽는 코드 } public void processFile() { System.out.println("Processing file contents..."); // 파일 내용을 처리하는 코드 } public void closeFile() { System.out.println("Closing file..."); // 파일을 닫는 코드 } } // 메인 클래스 public class Main { public static void main(String[] args) { FileProcessor fileProcessor = new FileProcessor(); String fileName = "example.txt"; // 파일을 열고, 읽고, 처리한 후, 닫는 절차적 작업 수행 fileProcessor.openFile(fileName); fileProcessor.readFile(); fileProcessor.processFile(); fileProcessor.closeFile(); } }
■통신적 응집도 -> 내부 요소들이 동일한 데이터를 사용하거나, 같은 데이터 구조를 조작할 때 발생, 모듈 내의 여러 기능이 동일한 데이터 집합을 중심으로 작업을 수행하는 경우class DataHandler { private List<String> data; public DataHandler() { data = new ArrayList<>(); } public void loadData() { System.out.println("Loading data..."); data.add("Data1"); data.add("Data2"); data.add("Data3"); } public void processData() { System.out.println("Processing data..."); for (int i = 0; i < data.size(); i++) { data.set(i, data.get(i).toUpperCase()); } } public void saveData() { System.out.println("Saving data..."); for (String item : data) { System.out.println("Saving: " + item); } } public void printData() { System.out.println("Printing data..."); for (String item : data) { System.out.println("Data: " + item); } } } // 메인 클래스 public class Main { public static void main(String[] args) { DataHandler dataHandler = new DataHandler(); // 데이터를 중심으로 여러 작업 수행 dataHandler.loadData(); dataHandler.processData(); dataHandler.saveData(); dataHandler.printData(); } }
■순차적 응집도 -> 각 기능의 출력이 다음 기능의 입력으로 사용되며, 기능들이 데이터 흐름에 따라 밀접하게 연결되어 있다.(각 모듈들의 데이터 의존성이 있어서 절차적 응집도와 차이가 있음)class TextProcessor { private String text; public void readText(String input) { System.out.println("Reading text..."); text = input; } public void processText() { System.out.println("Processing text..."); if (text != null) { text = text.toUpperCase(); } else { System.out.println("No text to process."); } } public void printText() { System.out.println("Printing text..."); if (text != null) { System.out.println("Processed Text: " + text); } else { System.out.println("No text to print."); } } } // 메인 클래스 public class Main { public static void main(String[] args) { TextProcessor textProcessor = new TextProcessor(); // 텍스트를 읽고, 처리하고, 출력하는 순차적 작업 수행 textProcessor.readText("Hello, world!"); textProcessor.processText(); textProcessor.printText(); } }
***
절차적 응집도와 순차적 응집도는 모듈 내의 기능들이 관련된 정도와 그 방식에서 차이가 있습니다. 둘 다 모듈 내의 기능들이 순서대로 실행된다는 점에서 유사하지만, 그 내부의 구조적 특징과 응집력에서 차이를 보입니다.
절차적 응집도 (Procedural Cohesion)
절차적 응집도는 모듈 내의 기능들이 순서대로 실행되지만, 각 기능이 특정 순서에 의존하기보다는 단순히 논리적인 흐름에 따라 배치된 경우를 말합니다. 각 기능이 서로 직접적인 데이터 의존성을 가지지 않고, 순서에 따라서 실행된다는 점에서 순차적 응집도보다 낮은 응집도를 가집니다.
- 특징: 기능들이 순서대로 배치되어 있지만, 각 기능이 독립적이며 다른 기능의 출력에 의존하지 않습니다.
- 예시: 초기화 작업, 설정 로드, 리소스 할당 등 논리적으로 순서대로 실행되는 작업들.
순차적 응집도 (Sequential Cohesion)
순차적 응집도는 모듈 내의 기능들이 순서대로 실행되고, 각 기능의 출력이 다음 기능의 입력으로 사용되는 경우를 말합니다. 이는 각 기능이 서로 밀접하게 연관되어 있으며, 한 작업이 완료된 후 그 결과가 다음 작업의 입력으로 이어지는 구조를 갖습니다.
- 특징: 각 기능이 순서대로 실행되며, 한 기능의 출력이 다음 기능의 입력으로 사용됩니다.
- 예시: 데이터 읽기, 데이터 처리, 데이터 저장 등 연속된 작업들이 데이터 흐름에 따라 순차적으로 실행되는 경우.
■기능적 응집도 -> 모듈 내 모든 요소가 하나의 명확한 기능을 수행하기 위해 협력하는 경우
가장 높은 수준의 응집도로 모듈이 하나의 특정 작업이나 기능을 수행하는데 집중되어 있다.class Adder { public int add(int a, int b) { return a + b; } } // 메인 클래스 public class Main { public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5, 3); System.out.println("The result is: " + result); } }
728x90반응형'전산학 > 소프트웨어공학' 카테고리의 다른 글
기능적 요구사항 vs 비기능적 요구사항 (0) 2024.08.01 상향식 비용 산정기법 vs 하향식 비용 산정기법 (0) 2024.08.01 비즈니스 관리 용어: RTO, RPO, MTD, WRT, MTRS (0) 2024.08.01 스토리보드, 목업 비교하기 (0) 2024.08.01 객체지향 5대 원칙 - SOLID (단일책임원칙, 개방폐쇄원칙, 리스코프치환 원칙, 인터페이스분리원칙, 의존역전원칙) (0) 2024.07.30