예시같은거 외어라!
모듈과 모듈 사이의 의존 정도
→ 낮을수록 좋음
ex) 핸들을 통해서 바퀴를 조종한다고 하지만, 타이어를 보수한다고 핸들까지 교체할 필요는 없다!

class Bill { //주차 요금 청구서
public static int getBillFee(int time, int discount) {
double discountPercentage = discount / 100;
//매서드 안에서 또 다른 메서드를 호출해 의존되고 있긴 하지만, 메서드에
//단순 파라미터 데이터를 보내는 형태
return Fee.calculateFee(time) * discountPercentage;
}
}
class Fee { //주차 요금 계산 모듈
public static int calculateFee(int time) {
int defaultMoney = 1000;
return defaultMoney * time;
}
}
public class Main{
public static void main(String[] args){
Bill.getBillFee(2,80); //2시간 이용, 80% 할인
}
}

// 이용기록 자료구조 형태
class Record {
public int carNum;
public int useTime;
private int fee;
Record(int carNum, int useTime){
this.carNum = carNum;
this.useTiem =useTime;
}
public void setFee(int fee) {
this.fee += fee;
}
public int getFee() {
return this.fee;
}
}
class BillFee {
//주차장 이용 청구서 작성 모듈
public static int getBillFee(String name, int hour){
Record record = new Record(name, hour); //객체 생성
Bill.calculateFee(record); //객체를 메서드에 넘김
Fee.calculateFeeAnoter(record); // 객체를 메서드에 넘김
return record.getFee();
}
}
class Bill {
//주차비용 계산 모듈
public static void calculateFee(Record record) {
int defaultMoney = 1000;
record.setFee(defaultMoney * record.useTime);
}
}
class Fee {
//기타비용 계산 모듈
public static int calcutateFeeAnother(Record record) {
int defaultMoney = 5000;
record.setFee(defaultMoney * record.useTime);
}
}
record 객체를 생성하여 메서드에 넘기므로 스탬프 결합이다.
//이용기록 자료구조 형태
class Record {
public int carNum;
~~public int useTime;~~
public int useMin;
private int fee;
Record(int carNum, int useTime){
this.carNum = carNum;
this.useTime = useTime;
}
public void setFee(int fee) {
this.fee += fee;
}
public int getFee() {
return this.fee;
}
}
레코드 변경 시, 다른 참조들도 변경해야하는 문제가 생긴다.

fee가 쓸데없이 조건 제어에 의존한다!
class User {
...
} // 회원 정보 클래스
class Bill { // 주차 요금 청구서 모듈
public static int getBillFee(int time) {
double discountPercentage = discount / 100;
User user1 = new User();
// 인수값에 따라서 모듈 내부 로직의 처리가 달라지는 결합 형태
return Fee.calculateFee(time, user.isJoin) * discountPercentage;
}
}
class Fee { // 주차 요금 계산 모듈
public static int calculateFee(int time, boolean isJoin) {
int defaultMoney = 1000;
// 회원 여부에 따라 주차요금을 계산하는 로직이 달라짐
if (isJoin) {
// ...
} else {
// ...
}
return defaultMoney * time - 400; // 회원이면 400원 할인
}
}
user.isJoin의 값에 따라서 다른 모듈 내부 로직의 처리가 달라지므로 제어 결합이다.
public class DataProcessor {
public void processData(int[] data, boolean isVerbose) {
if (isVerbose) {
System.out.println("Processing started...");
}
for (int i = 0; i < data.length; i++) {
data[i] *= 2;
}
if (isVerbose) {
System.out.println("Processing complete.");
}
}
}
public class Main{
public static void main(String[] args){
int[] data = {1,2,3};
DataProcessor processor = new DataProcessor();
processor.processData(data,true);
}
}
public class DataProcessor {
public void processData(int[] data) {
for (int i = 0; i < data.length; i++) {
data[i] *= 2;
}
}
}
public class DataProcessorLogger {
private DataProcessor processor;
public DataProcessorLogger(DataProcessor processor) {
this.processor = processor;
}
public void processWithLogging(int[] data) {
System.out.println("Processing started...");
processor.processData(data);
System.out.println("Processing complete.");
}
}
public class Main {
public static void main(String[] args) {
int[] data = {1, 2, 3};
DataProcessor processor = new DataProcessor();
DataProcessorLogger logger = new DataProcessorLogger(processor);
logger.processWithLogging(data);
}
}

class Addfee {
private int plusFee;
private int plusTime;
public Addfee(int plusFee, int plusTime) {
this.plusFee = plusFee;
this.plusTime = plusTime;
}
public int getPlusFee() {
return plusFee;
}
public int getPlusTime() {
return plusTime;
}
}
// 주차 요금 계산 모듈
class Fee {
public int calculateFee(int time, Addfee f) {
int defaultMoney = 1000;
return defaultMoney * (time + f.getPlusTime());
}//자기가 계산해도 되는데, 굳이 외부에서 가져옴 -> 외부가 바뀌면 자기도 바뀜..
}
// 요금 청구 모듈
class Bill {
private Fee fee;
public Bill() {
this.fee = new Fee();
}
public int getBillFee(int time, int discount, Addfee f) {
double discountPercentage = discount / 100.0;
return (int) (fee.calculateFee(time, f) * discountPercentage + f.getPlusFee());
}
}
모듈인 AddFee를 참조하여 최종 Fee를 결제하려 하고 있으므로 외부 결합이다.
그나마 개선 시도.
//그나마 개선한 것.
// 외부 모듈: 추가 요금 및 시간 정보
class Addfee {
private int plusFee;
private int plusTime;
public Addfee(int plusFee, int plusTime) {
this.plusFee = plusFee;
this.plusTime = plusTime;
}
public int getTotalExtraAmount() {
return plusFee;
}
public int applyToTime(int baseTime) {
return baseTime + plusTime;
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
int a = 1000; // 추가 요금
int b = 2; // 추가 시간
//추가 요금과 추가 시간은 각자 클래스에서 하는게 낫다 -> 개선이라 하긴 애매함..
Addfee addfee = new Addfee(a, b);
Bill bill = new Bill();
int totalFee = bill.getBillFee(2, 80, addfee);
System.out.println("최종 요금: " + totalFee + "원");
}
}
class Fee { // 주차 요금 계산 모듈
private static final int DEFAULT_MONEY = 1000;
public int calculate(int totalTime) {
return DEFAULT_MONEY * totalTime;
}
}
class Bill { // 요금 청구 모듈
private Fee fee;
public Bill(Fee fee) {
this.fee = fee;
}
public int getBillFee(int baseTime, int discountRate, Addfee addfee) {
int adjustedTime = addfee.applyToTime(baseTime);
int rawFee = fee.calculate(adjustedTime);
double discountedFee = rawFee * (discountRate / 100.0);
return (int) (discountedFee + addfee.getTotalExtraAmount());
}
}
그냥 애초에 자료결합으로 가는게 낫다.
AddFee와 같은 모듈 없애기. 외부 참조 자체를 없애기

class Discount {
public static int discount = 50; //공통 전역 데이터
public static int discountDefault = 500; //공통 전역 데이터
}
class Bill {
// 주차 요금 청구서 모듈
public int getBillFee(int time) {
double discountPercentage = Discount.discount / 100.0; // 공통 데이터 사용
return (int) (calculateFee(time) * discountPercentage);
}
private int calculateFee(int time) {
int defaultMoney = 1000 - Discount.discountDefault; // 공통 데이터 사용
return defaultMoney * time;
}
}
전역 변수의 값에 따라서 외부의 모듈 반환값까지 결정한다. 모듈이 매개변수 대신에 전역 변수를 이용하여 데이터를 교환하는 경우도 모두 공통 결합이다
class UserData {
public String name = "홍길동"; // 외부에서 직접 접근 가능
public int age = 30;
}
class UserProcessor {
public void makeUserOlder(UserData user) {
user.age += 10; // 내부 구현(필드)에 직접 접근해서 변경
if (user.age > 100) { // 비정상적인 내부 값 조작
user.name = "고령자 " + user.name;
}
}
}
public class Main {
public static void main(String[] args) {
UserData user = new UserData();
UserProcessor processor = new UserProcessor();
processor.makeUserOlder(user);
System.out.println(user.name + " (" + user.age + "세)");
}
}
UserData의 내부 필드에 직접 접근해서 변경하고 값을 조작하므로 내용 결합이다.
한 모듈 내부 요소들이 얼마나 잘 모여 있는가
→ 높을수록 좋음
public class Stack {
public void push(int element) { }
public int pop() { return 0; }
public int size() { return 0; }
}
모든 기능이 “stack” 자료구조를 위해 존재함! → 응집도 굳굳
class Sequential {
void processGrade(Grade grade) {
String numberGrade = grade.getGrade();
//바로 다음 입력값으로 이용
String letterGrade = grade.computeLetter(numberGrade);
grade.displayLetter(letterGrade);
}
}
class Communicational {
void Compute_MatrixMatrix(Matrix marix) {
int[][] aMatrix = marix.setGraph({
{0, 0, 0},
{0, 0, 0}
});
transform_matrix = marix.trans(aMatrix); //공통된 파라미터
inverse_matrix = marix.inverse(aMatrix); //공통된 파라미터
// 순서는 중요하지 않음
}
}
class Procedural extends Letter {
void sendLetter() {
this.writerBody();
this.writerSalutation();
this.send();
} //다른 기능이 하나로 엮여있다.
}
public class ApplicationInitializer {
public void initialize() {
loadConfiguration();
initializeLogger();
connectToDatabase();
initializeCache();
}
}
public class MessageHandler {
public void handleMessage(String messageType, String message) {
switch (messageType) {
case "EMAIL":
sendEmail(message);
break;
case "SMS":
sendSms(message);
break;
}
}
}
방정식이라는 점에서 비슷하지만, 방정식마다 역할이 다르므로 어차피 내가 나중에 선택할 것이므로
한번에 넣지말자.
-> 따로따로 객체를 만들어야지, 큰 하나의 객체를 만들고 그 안에서 결정하라는게 객체지향적이지 못함