본문 바로가기

코딩공부/JAVA

[15] JAVA 나의 앱 만들기 (2) - 조건문, 배열, 반복문, 메소드, 클래스, 인스턴스

조건문

이익(income)이 만원 이하인 경우에는 동업자1이 모든 이익을 가져가고, 만원을 초과할 경우에 동업자1:동업자2:동업자3 이 5:3:2의 비율로 이익을 나눠갖는 조건을 추가해보자

 

[실습]

AccountingApp.java  복&붙 -> rename : AccountingIFApp.java -> class명을 파일이름과 동일하게 변경 : 빨간줄 쳐진 클래스명위에 마우스오버 후, [Rename type to 'AccountingIFApp'] 클릭

 

dividendN 변수를 조건문 밖에서 미리 선언해줘야 한다.

public class AccountingIFApp {

	public static void main(String[] args) {
		
		double valueOfSupply = Double.parseDouble(args[0]);
        double vatRate = 0.1;	//부가세 비
        double expenseRate = 0.3;	//비용 
        double vat = valueOfSupply * vatRate;
        double total = valueOfSupply + vat;
        double expense = valueOfSupply * expenseRate;
        double income = valueOfSupply - expense; //이익 = 공급가 - 비용 
        
        //--> start of modified
        double dividend1;
        double dividend2;
        double dividend3;
        
        if (income > 10000) {
	        dividend1 = income * 0.5;
	        dividend2 = income * 0.3;
	        dividend3 = income * 0.2;
        } else {
        	dividend1 = income * 1.0;
	        dividend2 = income * 0;
	        dividend3 = income * 0;
        }
        //<-- end of modified
        
        System.out.println("123");
        System.out.println("Value of supply : " + valueOfSupply); //공급
        System.out.println("VAT : " + vat);	//VAT
        System.out.println("Total : " + total);	//합계(공급가 + VAT) : 소비자 지불 금
        System.out.println("Expense : " + expense);
        System.out.println("Income : " + income);
        System.out.println("Dividend 1 : " + dividend1);
        System.out.println("Dividend 2 : " + dividend2);
        System.out.println("Dividend 3 : " + dividend3);
 

	}
 
}

 

입력값을 50000.0 과 5000.0 으로 주고 실행해보자

income이 10000 원 이상인 경우
income이 10000원 이하인 경우

 

배열

이익배당률(0.5, 0.3, 0.2 값)에 이름을 붙이면 그 의미가 좀 더 분명해진다. 

rate라는 변수를 이용해서 코드를 수정하였다. 하지만 중간에 1억줄의 다른 코드가 들어간다면..?

rate라는 이름은 흔한 이름으로 중간에 들어간 코드에서 다른 값으로 변경이 되어 코드가 이상하게 동작할 수 있다. 즉, 변수가 많아질 수록 그 변수가 더렵혀질 가능성이 커진다는 문제점이 있다.

// 수정된 코드
	double rate1 = 0.5;
        double rate2 = 0.3;
        double rate3 = 0.2;
        //...code...
        double dividend1 = income * rate1; //이익 배당 
        double dividend2 = income * rate2;
        double dividend3 = income * rate3;

 

rate1, rate2, rate3 은 이름이 비슷한데 서로 같은 성격의 데이터라는 것이 분명하지 않기 때문에 "배열"을 이용할 수 있다.

이전과 똑같이 동작하는 코드이지만 배열을 도입함으로써 1) 이익배당률 각각의 값들이 서로 연관된 값이라는 것을 분명히 할 수 있게된다. 2) 이전에는 3개의 변수를 사용했지만 지금은 하나의 변수만 존재하기 때문에 이 변수가 더렵혀져 오염될 가능성이 낮아진다.

 

public class AccountingArrayApp {

	public static void main(String[] args) {
		
	double valueOfSupply = Double.parseDouble(args[0]);
        double vatRate = 0.1;	//부가세 비
        double expenseRate = 0.3;	//비용 
        double vat = valueOfSupply * vatRate;
        double total = valueOfSupply + vat;
        double expense = valueOfSupply * expenseRate;
        double income = valueOfSupply - expense; //이익 = 공급가 - 비용 
        
        //--> start of modified
        //double형의 데이터를 3개 담을 수 있는 double형 배열을 생
        double[] dividendRates = new double[3];
        dividendRates[0] = 0.5;
        dividendRates[1] = 0.3;
        dividendRates[2] = 0.2;
        
        double dividend1 = income * dividendRates[0]; //이익 배당 
        double dividend2 = income * dividendRates[1];
        double dividend3 = income * dividendRates[2];
        //<-- end of modified
        
        System.out.println("123");
        System.out.println("Value of supply : " + valueOfSupply); //공급
        System.out.println("VAT : " + vat);	//VAT
        System.out.println("Total : " + total);	//합계(공급가 + VAT) : 소비자 지불 금
        System.out.println("Expense : " + expense);
        System.out.println("Income : " + income);
        System.out.println("Dividend 1 : " + dividend1);
        System.out.println("Dividend 2 : " + dividend2);
        System.out.println("Dividend 3 : " + dividend3);
 

	}
 
}

 

 

반복문

동업자가 만명이라고 생각해보자. 동업자에게 나눠준 이익배당금을 출력하는 코드가 동업자 수만큼 만줄이 추가가 되어야한다. 코드의 추가와 수정이 힘들어질 것이다. 이 때 사용할 수 있는 것이 "반복문"이다.

System.out.println("Dividend 1 : " + dividend1);
System.out.println("Dividend 2 : " + dividend2);
System.out.println("Dividend 3 : " + dividend3);

 

동일한 작업을 수행하는 위 코드를 dividendRates 라는 배열에 담긴 수만큼 출력해주도록 반복문을 이용해서 한 줄의 코드로 짜보자

public class AccountingArrayLoopApp {

	public static void main(String[] args) {
		
	double valueOfSupply = Double.parseDouble(args[0]);
        double vatRate = 0.1;	//부가세 비
        double expenseRate = 0.3;	//비용 
        double vat = valueOfSupply * vatRate;
        double total = valueOfSupply + vat;
        double expense = valueOfSupply * expenseRate;
        double income = valueOfSupply - expense; //이익 = 공급가 - 비용 
        
         
        System.out.println("123");
        System.out.println("Value of supply : " + valueOfSupply); //공급
        System.out.println("VAT : " + vat);	//VAT
        System.out.println("Total : " + total);	//합계(공급가 + VAT) : 소비자 지불 금
        System.out.println("Expense : " + expense);
        System.out.println("Income : " + income);
        
        //--> start of modified
        //double형의 데이터를 3개 담을 수 있는 double형 배열을 생
        double[] dividendRates = new double[3];
        dividendRates[0] = 0.5;
        dividendRates[1] = 0.3;
        dividendRates[2] = 0.2;
        
//        double dividend1 = income * dividendRates[0]; //이익 배당 
//        double dividend2 = income * dividendRates[1];
//        double dividend3 = income * dividendRates[2];
        
        
        int i = 0; //몇번 반복되어야 하는지 체크하는 변
        while( i < dividendRates.length) { //반복이 되는 조건
        	System.out.println("Dividend 1 : " + income * dividendRates[i]);
        	i = i + 1;
        }
        //<-- end of modified
 
	}
 
}

 

메소드

메소드 란?

서로 연관된 코드를 그룹핑해서 이름을 붙인 정리정돈의 상자

 

부가세(VAT)를 구하는 코드가 1만개의 줄이라고 생각해보자. main 코드가 굉장히 복잡해질 것이다. getVAT()라는 메소드에 부가세를 구하는 코드를 작성하고, 부가세를 사용할 때는 getVAT()를 이용해서 가져오는 코드로 수정해보자

 

vat 구하는 코드 "valueOfSupply * vatRate"를 드래그하여 선택한 뒤, 우클릭 > [Refactor] > [Extract Method ... ] 

 

Mathod name : 만들려는 메소드의 이름

Replace 1 additional occurrence of statements with method 체크 해제 후 [ok]

 

이클립스가 자동으로 메소드를 만들어 준다.

 

▶ getVAT(valueOfSupply, expenseRate) 로 호출하지 않고, getVAT() 로 인자값없이 호출해서 사용하고 싶다면?

그냥 getVAT() 로 코드만 수정하면 안된다. valueOfSupply 와 expenseRate 는 main 메소드의 중괄호({}) 안에서 선언 되었다. 그렇기 때문에 main 메소드 안에서만 사용할 수 있는 지역의 변수 "지역변수(Local Valuable)" 이다. 

 그렇다면 어떻게 해야할까?

valueOfSupply 와  expenseRate 를 AccountingMethodApp 클래스의 "전역변수(Field)"로 선언해서 모든 메소드에서 접근할 수 있도록 해야한다.

 

전역변수로 변경할 변수 선택 후, 우클릭 > [Refactor] > [Convert Local Variable to Field ...]

 

Access modifier : public 선택 후 [ok]

 

이렇게 main클래스 밖인 AccountingMethodApp 클래스의 전역변수로 선언된 변수는 main 메소드와 getVAT 메소드 둘다에서 접근할 수 있게 된다.

public class AccountingMethodApp {
	public static double valueOfSupply; //전역변수 선언
	public static double vatRate;
	
	public static void main(String[] args) {
		
//	double valueOfSupply = Double.parseDouble(args[0]); //지역변수
	valueOfSupply = 10000.0; //변수값 초기화
        vatRate = 0.1;
        double vat = getVAT();
        //...code....
        
	}

    public static double getVAT() {
        return valueOfSupply * vatRate;
    }
	 
 
}

 

 

전체 코드를 메소드화

public class AccountingMethodApp {
	public static double valueOfSupply; //전역변수 선언
	public static double vatRate;
	public static double expenseRate;
	
	public static void main(String[] args) {
		
//		double valueOfSupply = Double.parseDouble(args[0]); //지역변수
		valueOfSupply = 10000.0; //변수값 초기화
        vatRate = 0.1;
        expenseRate = 0.3;
        print();
        
	}
	
	public static void print() {
		System.out.println("Value of supply : " + valueOfSupply);
		System.out.println("VAT : " + getVAT());
		System.out.println("Total : " + getTotal());
		System.out.println("Expense : " + getExpense());
		System.out.println("Income : " + getIncome());
		System.out.println("Dividend 1 : " + getDiviend1());
		System.out.println("Dividend 2 : " + getDiviend2());
		System.out.println("Dividend 3 : " + getDiviend3());
	}

        public static double getDiviend1() {
                return getIncome() * 0.5;
            }
        public static double getDiviend2() {
            return getIncome() * 0.3;
        }
        public static double getDiviend3() {
            return getIncome() * 0.2;
        }

        public static double getIncome() {
            return valueOfSupply - getExpense();
        }

        public static double getExpense() {
            return valueOfSupply * expenseRate;
        }

        public static double getTotal() {
            return valueOfSupply + getVAT();
        } 

        public static double getVAT() {
            return valueOfSupply * vatRate;
        }
	 
 
}

 

이렇게 메소드를 이용해서 사용하는 쪽에서 단정한 코드를 만들 수 있게 되었다.

 

클래스

클래스 란?

서로 연관된 변수와 메소드를 그룹핑하여 이름을 붙인 것

 

▶ 메소드, 클래스 가 중요한 이유?

소프트웨어를 만들어 가는데 있어서 구조를 결정하기 때문에 중요하다.

 

우리가 만든 클래스는 돈계산과 관련된 코드인데, 복잡한 프로그램의 경우 돈계산과 관련되지 않는 다른 취지의 코드들이 섞여 있을 수 있다. 그러면 클래스 내의 메소드와 변수가 1억개씩 있게 되면, 코드가 엉망진창이 되면서 우리가 프로그램을 만드는 생산성을 급격히 떨어뜨리는 방해요소가 될 수 있다. 이러한 상황에서 "클래스"를 이용할 수 있다. 파일의 디렉토리와 같은 역할이라고 보면 된다.

 

outline : 클래스 안에 소속되어있는 여러가지 멤버(클래스 소속의 변수, 메소드등)들의 리스트를 보여준다.

[Window] > [show view] > [outline]

 

  1. Accounting 클래스를 만들고, AccountingClassApp 소속인 필드(전역변수)를 컷해서 Accounting  클래스의 멤버로 변경하자.
    메소드들 또한 Accounting 클래스의 멤버로 변경한다.
  2. main 에서 Accouting 클래스의 멤버를 사용하기위해서 앞에 Accounting.변수 또는 Accounting.메소드() 와 같이 앞에 클래스명을 붙여서 사용하면 된다.
class Accounting{
	public static double valueOfSupply; 
	public static double vatRate;
	public static double expenseRate;
	
	public static void print() {
		System.out.println("Value of supply : " + valueOfSupply);
		System.out.println("VAT : " + getVAT());
		System.out.println("Total : " + getTotal());
		System.out.println("Expense : " + getExpense());
		System.out.println("Income : " + getIncome());
		System.out.println("Dividend 1 : " + getDiviend1());
		System.out.println("Dividend 2 : " + getDiviend2());
		System.out.println("Dividend 3 : " + getDiviend3());
	}

	public static double getDiviend1() {
	        return getIncome() * 0.5;
	    }
    public static double getDiviend2() {
        return getIncome() * 0.3;
    }
    public static double getDiviend3() {
        return getIncome() * 0.2;
    }
 
    public static double getIncome() {
        return valueOfSupply - getExpense();
    }
 
    public static double getExpense() {
        return valueOfSupply * expenseRate;
    }
 
    public static double getTotal() {
        return valueOfSupply + getVAT();
    } 
 
    public static double getVAT() {
        return valueOfSupply * vatRate;
    }
}

public class AccountingClassApp {

	public static void main(String[] args) {
		
		Accounting.valueOfSupply = 10000.0; 
		Accounting.vatRate = 0.1;
		Accounting.expenseRate = 0.3;
		Accounting.print();
        // anotherVariable = ...;
        // anotherMethod = ...;
        
	}
 
}

 

※ 클래스를 사용함으로써 얻을 수 있는 효과

이전과 똑같이 동작하지만 구조는 완전히 달라진 코드를 만들었다. 결과는 완전히 같다. 하지만,

Accounting에 속해있는 valueOfSupply , Accounting에 속해있는 print() 라는 것을 분명히 함으로써 그 소속관계를 명확히 할 수 있게 된 것이다. 이제 다른 취지의 코드들과 뒤섞여도 상관없고 print() 와 같이 흔한 이름의 메소드를 사용해도 클래스만 다르다면 같은 이름의 메소드들이 공존할 수 있게 된다. 

 

 

인스턴스

인스터스 란?

하나의 클래스를 복제해서 서로 다른 데이터의 값과 서로 같은 메소드를 가진 복제본을 만드는 것

 

Accounting의 내부적인 상태(변수의 값)가 수십 개를 세팅해서 수십개의 메소드가 호출이 됐다. 어쩌다가 한 번 다른 제품이 들어온다면.. ? 다른 제품에 대한 값으로 변수들을 셋팅하고, 메소드를 호출하고... 다시 원래 제품의 상태로 쓰려면 앞서 해놨던 세팅을 다시 해야한다.

 

public class AccountingClassApp {

	
	public static void main(String[] args) {
		
		Accounting.valueOfSupply = 10000.0; 
		Accounting.vatRate = 0.1;
		Accounting.expenseRate = 0.3;
		//...code...
		Accounting.print();
		//...code...
		Accounting.valueOfSupply = 20000.0; 
		Accounting.vatRate = 0.05;
		Accounting.expenseRate = 0.2;
		Accounting.print();
		
		Accounting.valueOfSupply = 10000.0; 
		Accounting.vatRate = 0.1;
		Accounting.expenseRate = 0.3;
		//...code...
		Accounting.print();
	}
	
}

 

이 과정이 빈번하게 발생한다고 한다면, 이렇게 클래스의 내부적인 상태를 바꾸는 행위가 버그를 유발할 가능성이 굉장히 높다.

이 문제를 Accounting1 , Accounting2 클래스를 별도로 만들어서 해결할 수도 있다.

Accounting2에 변수값을 세팅 후, Accounting1.print()를 하여도 에러없이 잘 수행된다. Accounting1의 내부적인 상태는 변경되지 않기 때문이다.

class Accounting1{
	public static double valueOfSupply; 
	public static double vatRate;
	public static double expenseRate;
	
	public static void print() {
		System.out.println("Value of supply : " + valueOfSupply);
		System.out.println("VAT : " + getVAT());
		System.out.println("Total : " + getTotal());
		System.out.println("Expense : " + getExpense());
		System.out.println("Income : " + getIncome());
		System.out.println("Dividend 1 : " + getDiviend1());
		System.out.println("Dividend 2 : " + getDiviend2());
		System.out.println("Dividend 3 : " + getDiviend3());
	}

	public static double getDiviend1() {
	        return getIncome() * 0.5;
	    }
    public static double getDiviend2() {
        return getIncome() * 0.3;
    }
    public static double getDiviend3() {
        return getIncome() * 0.2;
    }
 
    public static double getIncome() {
        return valueOfSupply - getExpense();
    }
 
    public static double getExpense() {
        return valueOfSupply * expenseRate;
    }
 
    public static double getTotal() {
        return valueOfSupply + getVAT();
    } 
 
    public static double getVAT() {
        return valueOfSupply * vatRate;
    }
}
class Accounting2{
	public static double valueOfSupply; 
	public static double vatRate;
	public static double expenseRate;
	
	public static void print() {
		System.out.println("Value of supply : " + valueOfSupply);
		System.out.println("VAT : " + getVAT());
		System.out.println("Total : " + getTotal());
		System.out.println("Expense : " + getExpense());
		System.out.println("Income : " + getIncome());
		System.out.println("Dividend 1 : " + getDiviend1());
		System.out.println("Dividend 2 : " + getDiviend2());
		System.out.println("Dividend 3 : " + getDiviend3());
	}

	public static double getDiviend1() {
	        return getIncome() * 0.5;
	    }
    public static double getDiviend2() {
        return getIncome() * 0.3;
    }
    public static double getDiviend3() {
        return getIncome() * 0.2;
    }
 
    public static double getIncome() {
        return valueOfSupply - getExpense();
    }
 
    public static double getExpense() {
        return valueOfSupply * expenseRate;
    }
 
    public static double getTotal() {
        return valueOfSupply + getVAT();
    } 
 
    public static double getVAT() {
        return valueOfSupply * vatRate;
    }
}

public class AccountingClassApp {

	
	public static void main(String[] args) {
		
		Accounting1.valueOfSupply = 10000.0; 
		Accounting1.vatRate = 0.1;
		Accounting1.expenseRate = 0.3;
		Accounting1.print();

		Accounting2.valueOfSupply = 20000.0; 
		Accounting2.vatRate = 0.05;
		Accounting2.expenseRate = 0.2;
		Accounting2.print();
		
		Accounting1.print(); //<--
	}
	
}

 

" 클래스를 복제해서 서로 다른 상태, 서로 같은 메소드를 갖도록 한 것 "

하나의 클래스로 돌려막기한 것이 아니라, 복제된 클래스를 이용해서 더 편리하고 더 버그가 적은 코딩을 할 수 있게 된다는 것이다.

 

하지만, 코드가 중복되고 있다. 클래스의 메소드를 바꾸려면 복제한 모든 클래스의 메소드를 바꿔줘야 한다.

"중복을 줄이고, 경제성을 높이자"  자바에서 클래스를 복제할 수 있는 기능이 있다. 이 코드를 인스턴스를 이용해서 바꿔보자

인스턴스 : new 명령으로 복제한 클래스의 복제본

 

new 클래스() : 클래스를 복제하는 명령

Accounting a1 = new Accounting(); Accounting클래스를 복제해서 a1이라는 변수에 넣고, a1의 데이터타입은 Accounting 으로 선언

* 데이터 타입 : Accounting의 복제본만 들어올 수 있다고 알려주는 것

Accounting a1 = new Accounting();
a1.valueOfSupply = 10000.0;
a1.vatRate = 0.1;
a1.expenseRate = 0.3;
a1.print();

 

그리고 Accounting 클래스에 "static" 키워드가 사용되면 안되므로 전부 지워준다.

class Accounting{
	public double valueOfSupply; 
	public double vatRate;
	public double expenseRate;
	
	public void print() {
		System.out.println("Value of supply : " + valueOfSupply);
		System.out.println("VAT : " + getVAT());
		System.out.println("Total : " + getTotal());
		System.out.println("Expense : " + getExpense());
		System.out.println("Income : " + getIncome());
		System.out.println("Dividend 1 : " + getDiviend1());
		System.out.println("Dividend 2 : " + getDiviend2());
		System.out.println("Dividend 3 : " + getDiviend3());
	}

	public double getDiviend1() {
	        return getIncome() * 0.5;
	    }
    public double getDiviend2() {
        return getIncome() * 0.3;
    }
    public double getDiviend3() {
        return getIncome() * 0.2;
    }
 
    public double getIncome() {
        return valueOfSupply - getExpense();
    }
 
    public double getExpense() {
        return valueOfSupply * expenseRate;
    }
 
    public double getTotal() {
        return valueOfSupply + getVAT();
    } 
 
    public double getVAT() {
        return valueOfSupply * vatRate;
    }
}

public class AccountingClassApp {

	public static void main(String[] args) {	
		// instance
		Accounting a1 = new Accounting();
		a1.valueOfSupply = 10000.0;
		a1.vatRate = 0.1;
		a1.expenseRate = 0.3;
		a1.print();
		
		Accounting a2 = new Accounting();
		a2.valueOfSupply = 20000.0;
		a2.vatRate = 0.05;
		a2.expenseRate = 0.2;
		a2.print();
		
		a1.print();
	}
	
}