1. Spring 프레임워크의 "프레임 워크"란 무엇인가?



프레임워크는 

비기능적 요구사항을 만족하는 구조와 구현된 기능을 안정적으로 실행하도록 제어 해주는 

잘 만들어진 구조의 라이브러리 덩어리이다.


프레임워크는 애플리케이션들의 최소한의 공통점을 찾아 하부 구조를 제공함으로써 개발자로

하여금 시스템의 하부구조를 구현하는데 들어가는 노력을 절감해준다.




2. 프레임워크를 왜 사용할까?



비기능적인 요소들을 초기 개발 단계마다 구현해야 하는 불합리함을 극복해준다.

기능적인 요구사항에 집중 할 수 있도록 해준다.

디자인 패턴과 마찬가지로 반복적으로 발견되는 문제를 해결하기 위한 특화된 solution을 제공한다.




3. 프레임워크는 디자인패턴과 관련이 있나?



프레임워크의 핵심적인 특징이 디자인 패턴이다. 

하지만 프레임워크는 디자인 패턴이 아니다.


디자인패턴은 애플리케이션을 설계할 때 필요한 구조적인 가이드라인이 되어 줄수는 있지만 구체적으로

구현된 기반코드를 제공하지 않는다.


프레임워크는 디자인 패턴과 함께 패턴이 적용된 기반 클래스 라이브러리를 제공해서 

프레임워크를 사용하는 구조적인 틀과 구현코드를 함께 제공한다.




4. 프레임워크의 구성요소와 종류



IoC(Inversion of Control)


IoC란 "제어의 역전" 즉 인스턴스 생성부터 소멸까지의 인스턴스 생명주기 관리를 

개발자가 아닌 컨테이너가 대신 해준다는 뜻이다.

즉 컨테이너 역할을 해주는 프레임워크에게 제어하는 권한을 넘겨서 개발자의 코드가

신경써야 할 것을 줄이는 전략이다.



프레임워크의 동작원리를 제어흐름이 일반적인 프로그램 흐름과 반대로 동작하므로 IoC라고 한다.


Spring 컨테이너는 IoC를 지원하며, 메타데이터(XML설정)을 통해 beans를 관리하고

어플리케이션의 중요부분을 형성한다.


Spring 컨테이너는 관리되는 bean들을 의존성주입(Dependency Injection)을 통해

IoC를 지원한다.




5. 프레임워크와 라이브러리의 차이점


프레임워크는 특정부분의 기술적인 구현을 라이브러리 형태로 제공한다.

Class Library라는 구성요소는 프레임워크의 정의 중 하나인 Semi Complete(반제품)이다.



 특징

 프레임워크

 라이브러리

 유저코드의 작성

 프레임워크 클래스를 서브클래싱해서 작성

 독립적으로 작성

 호출흐름

 프레임워크코드가 유저코드를 호출

 유저코드가 라이브러리를 호출

 실행흐름

 프레임워크가 제어

 유저코드가 제어

 객체의 연동

 구조프레임워크가 정의

 독자적으로 정의



- 프레임워크와 라이브러리를 구분하는 방법은 실행제어가 어디서 일어나는 가에 달려있다.

- 라이브러리는 개발자가 만든 클래스에서 직접 호출하여 사용하므로 실행의 흐름에 대한 제어를 개발자의 코드가 관장하고 있다.

- 프레임워크는 반대로 프레임워크에서 개발자가 만든 클래스를 호출하여 실행의 흐름에 대한 제어를 담당한다.


디자인 패턴 + 라이브러리 = 프레임워크


- 프레임워크는 디자인 패턴과 그것이 적용된 기반 라이브러리의 결합이다.

- 프레임워크를 확장하거나 커스터마이징 할때는 프레임워크에 적용된 패턴에 대한 이해가 필요하다.





module-info.java를 통해 

서로 다른 프로젝트 a, b에 대해 a에서 선언한 클래스를 b에서 불러와 사용할수 있다.


common.widget프로젝트에는 

com.logicbig 패키지에 RendererSupport.java라는 파일이 있고

org.jwidgets 패키지에 SimpleRenderer.java라는 파일이 있다.


common.widget 프로젝트(a)의 module-info.java에 외부에 노출하고 싶은 패키지를 exports해주고



1
2
3
module common.widget {
    exports com.logicbig;    // 외부에 노출하고싶은 패키지
}
cs




패키지를 require시 접근하여 사용할수 있다.




1
2
3
4
module data.widget {
    requires common.widget;    // 사용할 패키지
    requires java.sql;
}
cs




requires로 선언후 Java Build Path로 Module path를 잡아주면된다.






이후 프로젝트(b)에서 다른 프로젝트(a)에 있는 클래스를 호출하여 객체를 생성하거나 메소드를 활용할 수 있다.



프로젝트 a의 RendererSupport.java


1
2
3
4
5
6
7
8
9
package com.logicbig;
 
import org.jwidgets.SimpleRenderer;
 
public class RendererSupport {
  public void render(Object object) {
      new SimpleRenderer().renderAsString(object);
  }
}
cs




프로젝트 a의 SimpleRenderer.java


1
2
3
4
5
6
7
package org.jwidgets;
 
public class SimpleRenderer {
  public void renderAsString(Object object) {
      System.out.println(object);
  }
}
cs




프로젝트 b의 Component.java


1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example;
 
import com.logicbig.RendererSupport;
import java.sql.*;
 
// java.base 그 외 다른 모듈 패키지를 쓰려면 module-info에 require로 넣어줘야함.
 
public class Component {
  public static void main(String[] args) {
      RendererSupport support = new RendererSupport();
      support.render("Test Object");
  }
}
cs




프로젝트 b의 Component.java파일에서 프로젝트 a의 RenderSupport 클래스를 호출하여 객체를 생성하는 것을 확인할 수 있다.



module-info.java에 대해 공부하여 서로 다른 프로젝트 간의 exports, require에 대해 알 수 있었다.



소스코드 첨부 :


common.widget.zip

data.widget.zip




자바8의 Time 및 Date 관련 새로운 API를 공부하게 되어 정리하게 되었다. 



1. LocalDate, LocalTime, LocalDateTime, DateTimeFormatter 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package dateTimeTest;
 
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
 
public class LocalDateTimeTest {
 
    public static void main(String[] args) {
 
        // 1. LocalDate 사용
        LocalDate today = LocalDate.now();
        System.out.println(today);
        
        System.out.println("년월일"+today.getYear() + "/" + today.getMonth() + " "+today.getMonthValue() 
        +"/"+today.getDayOfMonth() + " 지난일자 " + today.getDayOfYear());
        System.out.println("요일 " + today.getDayOfWeek()+" "+today.getDayOfWeek().getValue());
        
        // 특정 날짜를 지정해서 LocalDate 생성
        LocalDate endDay = LocalDate.of(20191231);
        System.out.println("현재 기준 몇일 남아 있는지 "+ today.until(endDay, ChronoUnit.DAYS));
        
        System.out.println("현재 기준 1개월 후 "+today.plusMonths(1));
        System.out.println(DayOfWeek.TUESDAY.plus(3));
        
        // LocalTime 사용
        LocalTime now = LocalTime.now();
        System.out.println(now);
        System.out.println("시분초나노초" + now.getHour() + " " + now.getMinute()
                                        + " " + now.getSecond() + " " +now.getNano());
        
        // 특정 시간을 지정해서 LocalTime 생성
        LocalTime bedTime = LocalTime.of(2340);
        LocalTime wakeTime = bedTime.plusHours(8);
        System.out.println(wakeTime);
        
        // LocalDateTime 사용
        LocalDateTime dt = LocalDateTime.now();
        System.out.println(dt);
 
        LocalDate date = dt.toLocalDate();
        System.out.println(date);
        
        LocalTime time = dt.toLocalTime();
        System.out.println(time);
        
        // 특정 날짜와 시간을 지정해서 LocalDateTime 생성
        LocalDateTime dt2 = LocalDateTime.of(2019,2,19,13,20,50);
        System.out.println(dt2);
        System.out.println(dt2.getMonth());
        
        dt2.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        
        // 포맷을 직접 설정한 Formatter 생성
        DateTimeFormatter myFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss E a", Locale.KOREAN);
        System.out.println(dt2.format(myFormat));    
    }
}
cs





2. LocalDateTime 응용해 현재로부터 월급날까지 남은 일수 계산하기



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package dateTimeTest;
 
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
 
public class SalaryDate {
    
    // 내 월급날(매월 25일)은 몇일 남았을까?
    public static void main(String[] args) {
         LocalDate today = LocalDate.now();
//        LocalDate today = LocalDate.of(2019, 2, 26);
        
        // 1. 기준이 되는 날짜를 구하기
        LocalDate theDay = LocalDate.from(today);
        System.out.println("오늘날짜 : "+theDay);
        
        // 2. 월급날 구하기 매월 25일 
        int salaryDay = 25;
        int remainDay = 0;
 
        if(theDay.getDayOfMonth() > salaryDay) {
            LocalDate lastDayOfMonth = theDay
                    .with(TemporalAdjusters.lastDayOfMonth());
            remainDay = lastDayOfMonth.getDayOfMonth()-theDay.getDayOfMonth()+salaryDay;
        }else if(theDay.getDayOfMonth() < salaryDay) {
            remainDay = salaryDay-theDay.getDayOfMonth();
        }else {
            remainDay = 0;
        }
        
        System.out.println("월급까지 "+remainDay+"일 남았습니다.");
    }
}
 
cs


1
2
오늘날짜 : 2019-02-20
월급까지 5일 남았습니다.
cs



소스코드 첨부: 


myLamdaTest.zip





1. 스트림이란?


스트림은 자바8부터 추가된 컬렉션의 저장요소를 하나씩 참조해서 람다식으로 처리할수 있도록 해주는 반복자이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package streamTest;
 
import java.util.Arrays;
import java.util.List;
 
public class myStreamTest {
 
    public static void main(String[] args) {
        int count1 = 0;
         List<String> names = Arrays.asList("1""2""3""4""5");
         
         // 기존의 for문
        for (String w : names) {
            if(w.length() > 0) {
                count1++;
            }
        }
        
        // stream 방식
        long count2 = 
                names.stream()
                         .filter(a->a.length()>0)
                         .count();
        
        // 병렬 stream 방식
        long count3 = 
                names.parallelStream()
                         .filter(a->a.length()>0)
                         .count();
        
        System.out.println(count1);
        System.out.println(count2);
        System.out.println(count3);
    }
}
cs


count1, count2, count3의 결과는 같다. 

기존for문,스트림, 병렬스트림 을 사용해서 표현한 것을 확인할 수 있다.



2. 스트림의 특징


- 스트림을 활용하면 복잡한 코드를 간결하게 표현할 수 있다.

- 스트림은 컬렉션, 배열, 파일 등의 대량의 데이터를 가공 축소하여 데이터의 합계, 

평균값, 카운팅, 최대값, 최소값, 집계함수를 모두 표현할 수 있다.


- 오리지날 스트림에서 중간처리(필터, 매핑)를 하고 최종처리(집계 처리 결과물)를 하여 결과를 볼수 있다.

- 스트림은 iterator와 비슷한 역할을 하는 반복자이다.


- 스트림이 제공하는 요소처리 메서드는 함수적 인터페이스 타입이므로 람다식, 

메서드 참조를 이용하여 요소처리 내용을 인자로 전달할 수 있다.



3. 컬렉션과 스트림의 공통점과 차이점



공통점


- 연속된 요소형식의 값을 저장하는 자료구조 인터페이스를 제공한다.

- 둘다 순차적으로 요소에 접근한다.


차이점


컬렉션은


- 각 계산식을 만날 때마다 데이터가 계산된다. 

- 데이터의 접근, 읽기, 변경, 저장이 주요 관심사이다.

- 데이터에 접근하는 방법을 직접 작성해야한다.

- Iterator로 모든요소를 순환해야한다.

- 메모리에 모든 요소가 올라가 있는 상태에서 요소들을 누적시키며 결과를 계산한다.

- 메모리 사용량이 늘어난다.


스트림은


- 최종 연산이 실행 될 때에 데이터가 계산된다. 

- 계산식(람다)을 표현하는 것이 주요 관심사이다.

- 데이터에 접근하는 방법이 추상화되어있다.

- 계산식을 미리 적어두고 계산시에 람다식으로 JVM에 넘긴다.

- 내부에서 요소들을 어떻게 메모리에 올리는 지는 관심사가 아니다.

- 메모리 사용량이 줄어든다.



스트림을 활용해 코드를 개선해보자.





4. 스트림 생성방법 예제



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package streamTest;
 
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.IntSupplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
 
public class MakingStreams {
 
    public static void main(String...args) throws Exception{
        
        // Stream.of
        Stream<String> stream = Stream.of("Java8""Lambdas""SangHyun");
        stream.map(String::toUpperCase).forEach(System.out::println);
        // result : UpperCase
        
        // Arrays.stream
        int[] numbers = {12345};
        System.out.println(Arrays.stream(numbers).sum());
        // result : 15
        
        // Stream.iterate
        Stream.iterate(0, n -> n + 2)
              .limit(5)
              .forEach(System.out::println);
        // result : 0 2 4 6 8
        
        // random stream of doubles with Stream.generate
        Stream.generate(Math::random)
              .limit(10)
              .forEach(System.out::println);
        // result : random data
        
        // stream of 1s with Stream.generate
        IntStream.generate(() -> 1)
                 .limit(5)
                 .forEach(System.out::println);
        // result : 1 1 1 1 1 
        
        IntStream.generate(new IntSupplier(){
            public int getAsInt(){
                return 2;
            }
        }).limit(5)
          .forEach(System.out::println);
        // result : 2 2 2 2 2
 
        // 피보나치 수열
        IntSupplier fib = new IntSupplier(){
                  private int previous = 0;
                  private int current = 1;
                  public int getAsInt(){
                      int nextValue = this.previous + this.current;
                      this.previous = this.current;
                      this.current = nextValue;
                      return this.previous;
                  }
              };
         IntStream.generate(fib).limit(10).forEach(System.out::println);
         // result : 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
         
         // 유니크 단어 카운트
         long uniqueWords = Files.lines(Paths.get("data2.txt"), Charset.defaultCharset())
                                 .flatMap(line -> Arrays.stream(line.split(" ")))
                                 .distinct()
                                 .count();
 
         System.out.println("There are " + uniqueWords + " unique words in data2.txt");
    }
}
cs




5. 스트림 활용하여 문제 풀기



5.1 Developer 모델



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package streamTest;
 
public  class Developer{
    
    private String name;
    private String title;
 
    public Developer(String n, String c){
        this.name = n;
        this.title = c;
    }
 
    public String getName(){
        return this.name;
    }
 
    public String getTitle(){
        return this.title;
    }
 
    public void setTitle(String newTitle){
        this.title = newTitle;
    }
 
    public String toString(){
        return "Developer:"+this.name + " , " + this.title;
    }
}
cs




5.2 BiddingCompany 모델



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package streamTest;
 
public class BiddingCompany{
 
    private Developer developer;
    private String companyName;
    private int year;
    private int salary;
 
    public BiddingCompany(Developer developer, String companyName, int year, int salary)
    {
        this.developer = developer;
        this.companyName = companyName;
        this.year = year;
        this.salary = salary;
    }
 
    public Developer getDeveloper(){ 
        return this.developer;
    }
    
    public String getCompanyName() {
        return companyName;
    }
 
    public int getYear(){
        return this.year;
    }
 
    public int getSalary(){
        return this.salary;
    }
    
    public String toString(){
        return "{" + this.developer + ", " +
               "companyName: "+this.companyName+", " +
               "year: "+this.year+", " +
               "salary:" + this.salary +"}";
    }
}
cs



5.3 StreamTestCode Main 코드



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package streamTest;
 
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
 
public class StreamTestCode {
    public static void main(String ...args){    
        // 개발자의 성명과 직급
        Developer sanghyun = new Developer("SangHyun""대리");
        Developer hyunYoung = new Developer("HyunYoung","사원");
        Developer jiSook = new Developer("JiSook","주임");
        Developer choa = new Developer("Choa","대리");
        
        // 입찰회사에서 개발자 채용을 위해 연봉제시 
        // 입찰한 개발자 이름, 참여한 회사이름, 설립연도 , 제시한연봉 
        List<BiddingCompany> company = Arrays.asList(
            new BiddingCompany(sanghyun, "쿠팡",20104000), 
            new BiddingCompany(sanghyun, "위메프",20115000), 
            new BiddingCompany(sanghyun, "sk플래닛",20127000), 
            new BiddingCompany(hyunYoung, "위메프",20102000),  
            new BiddingCompany(hyunYoung, "sk플래닛",20123000), 
            new BiddingCompany(jiSook, "쿠팡",20103500),
            new BiddingCompany(choa, "위메프",20113800
        );    
        
        // 쿼리 1 : 설립일이 2012년인 입찰회사를 모두 찾아서 연봉이 작은값부터 큰값으로 출력해라
        // 2012년이라는 특정한 조건이 있으므로 filter사용, 작은값부터 큰값 정렬 sorted 사용
        
        List<BiddingCompany> t1 = 
                company.stream()
                             .filter(a->a.getYear()==2012)
                             .sorted(Comparator.comparing(b->b.getSalary()))
                             .collect(Collectors.toList());
        
        t1.forEach(System.out::println);
        /* result : {Developer:HyunYoung , 사원, companyName: sk플래닛, year: 2012, salary:3000}
                        {Developer:SangHyun , 대리, companyName: sk플래닛, year: 2012, salary:7000} */
        
        // 쿼리 2 : 입찰회사 개발자 목록에서 개발자의 중복되지 않는 직급만 출력해라.
        // 특정한 조건이 딱 주어지지 않았으므로 map사용, 중복제거 distinct사용
        
        List<String>  t2_1 =     
                company.stream()
                             .map(BiddingCompany::getDeveloper)
                             .map(Developer::getTitle)
                             .distinct()
                             .collect(Collectors.toList());
 
        System.out.println("map 2번 사용");
        t2_1.forEach(System.out::println);
        /* result : 대리 사원 주임 */
        
        List<String>  t2_2 =         
                company.stream()
                             .map(tx -> tx.getDeveloper().getTitle()) 
                             .distinct()
                             .collect(Collectors.toList());
        
        System.out.println("map 1번 사용");
        t2_2.forEach(System.out::println);
        /* result : 대리 사원 주임 */
        
        // 쿼리 3: 개발자 중에서 대리직급을 찾아 이름순으로 정렬해라.
        
        company.stream()
                     .map(a->a.getDeveloper())
                     .filter(b->b.getTitle().equals("대리"))
                     .distinct()
                     .sorted(Comparator.comparing(c->c.getName()))
                     .forEach(System.out::println);
        /* result : Developer:Choa , 대리 
                        Developer:SangHyun , 대리 */
        
        // 쿼리 4: 알파벳 순서대로 정렬하여 개발자 이름만 출력해라
        
        String names = 
                company.stream()
                             .map(a->a.getDeveloper().getName())
                             .distinct()
                             .sorted()
                             .reduce("", (a1,a2) -> a1+a2+" ");
        
        System.out.println(names);
        /* result : Choa HyunYoung JiSook SangHyun */
        
        // 쿼리 5 : 사원 직급의 개발자가 있는가?
        
        boolean 사원 = 
                company.stream()
                             .anyMatch(tx -> tx.getDeveloper().getTitle().equals("사원"));
                                    
        System.out.println(사원);
        /* result : true */
        
        // 쿼리 6 : 대리직급의 개발자를 과장직급으로 업데이트 해라
        
        System.out.println("업데이트 전: "+company);
        company.stream()
                     .map(a->a.getDeveloper())
                     .filter(a->a.getTitle().equals("대리"))
                     .forEach(a->a.setTitle("과장"));
        System.out.println("업데이트 후: "+company);
        
        // 쿼리 7: 입찰한 모든회사 중에 가장 높은 연봉을 제시한 값은 얼마인가?
        
        int highSalary = 
                company.stream()
                             .mapToInt(a->a.getSalary())
                             .max()
                             .getAsInt();
        
        System.out.println(highSalary);
        /* result : 7000 */
    }
}
cs


소스 코드 첨부 :


myLamdaTest.zip




1. 람다식


람다는 하나의 abstract 메서드 만 가진 인터페이스를 함수형 인터페이스라고 한다.

람다식을 지원하는 java.util.functon 패키지가 추가되었다.



1.1 람다 표현과 특성


1
Runnable r = () -> System.out.println("Hello World");
cs


1
2
3
4
5
public interface Comparator<T> {
    public int compare(T o1, T o2);
}
 
Comparator<Integer> comparator = (o1, o2) -> o2 - o1;
cs


 - 이름이 없는 익명이다.

 - 메서드처럼 특정클래스에 종속되지 않는다. 

 - 람다식을 메서드의 인자로 전달하거나 변수값으로 저장할 수 있다.

 - 간결하다.

 



2. 스트림

 

자바8은 스트림과 람다를 활용하여 이전보다 간결하고 향상된 방법으로 collection을 처리한다.

 

stream은 순차(sequential), 병렬(parallel) 두 종류가 존재한다.

순차 stream은 싱글 쓰레드가 순차적으로 처리되며 병렬 stream은 fork/join 프레임워크로 구현된 

메서드에 의해 병렬로 처리된다.

 


2.1 stream의 장단점


장점은 Laziness, Collection의 interation처리를 자바에게 맡겨 둠으로써 jvm이 최적화할수 있는 기회를 제공한다.

단점은 재사용 불가하다. 한번 사용된 stream은 재사용이 불가능하여 필요에 따라 새롭게 만들어야 한다.

 


3. 함수형 인터페이스 활용하기


3.1 java.util.function 패키지의 함수형 인터페이스

 

함수형 인터페이스

 Descriptor

 Method명

 Predicate<T>

 T -> boolean

 test()

 Consumer<T>

 T -> void

 accept()

 Supplier<T>

 () -> T

 accept()

 Function<T,R>

 T -> R

 apply()

 UnaryOperator<T>

 T -> T

 identity()



위에 함수형 인터페이스를 활용해서 예제를 작성해보겠다.



3.2 predicate와 consumer 예제



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package lamdaTest;
 
public class Grape {
    private int weight = 0;
    private String color = "";
 
    public Grape(int weight, String color){
        this.weight = weight;
        this.color = color;
    }
 
    public Integer getWeight() {
        return weight;
    }
 
    public void setWeight(Integer weight) {
        this.weight = weight;
    }
 
    public String getColor() {
        return color;
    }
 
    public void setColor(String color) {
        this.color = color;
    }
 
    public String toString() {
        return "Grape{" +
               "color='" + color + '\'' +
               ", weight=" + weight +
               '}';
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package lamdaTest;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
 
public class PredicateAndConsumer {
 
    public static void main(String... args) {
 
        // 청포도, 보라포도, 적포도
        List<Grape> inventory = 
                Arrays.asList(new Grape(70"green"), 
                              new Grape(90"purple"), 
                              new Grape(120"red"));
 
        // 1. Predicate 인터페이스 사용
        System.out.println("----- 1. Predicate 인터페이스 사용 -----");
        filter(inventory, grape -> grape.getWeight() >=100)
        .forEach(System.out::println);
        // result : Grape{color='red', weight=120}
        
        // 2. Consumer 인터페이스 사용
        System.out.println("----- 2. Consumer 인터페이스 사용 -----");
        printGrapeInfo(inventory, grape -> System.out.println(grape));
        // result : All List print
        
    }
 
    public static void printGrapeInfo(List<Grape> inventory, Consumer<Grape> consumer) {
        for (Grape grape : inventory) {
            consumer.accept(grape);
        }
    }
    
    public static List<Grape> filter(List<Grape> inventory, Predicate<Grape> p) {
        List<Grape> result = new ArrayList<>();
        for (Grape grape : inventory) {
            if (p.test(grape)) {
                result.add(grape);
            }
        }
        return result;
    }
}
cs



3.3 supplier 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package lamdaTest;
 
import java.util.function.Supplier;
 
class Animal {
    public void eat() {
        System.out.println("Eating something");
    }
}
 
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("Eating fish");
    }
}
 
public class SupplierTest {
    static void eatSomething(Supplier<extends Animal> supplier) {
        Animal animal = supplier.get();
        animal.eat();
    }
 
    public static void main(String[] args) {
        // Using Lambda expression
        eatSomething(() -> new Animal());
        eatSomething(() -> new Cat());
        
        // Using Method Reference
        eatSomething(Animal::new);
        eatSomething(Cat::new);
    }
}
cs



3.4 function 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package lamdaTest;
 
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
 
public class FunctionTest {
 
    // Function 인터페이스
    public static List<String> getColorList(List<Grape> inventory, Function<Grape, String> function) {
        List<String> colorList = new ArrayList<String>();
        for (Grape grape : inventory) {
            colorList.add(function.apply(grape));
        }
        return colorList;
    }
 
    public static void main(String[] args) {
        List<Grape> grapeList = new ArrayList<>();
        grapeList.add(new Grape(150"green"));
        grapeList.add(new Grape(200"purple"));
        grapeList.add(new Grape(200"red"));
        grapeList.add(new Grape(150"red"));
 
        // 1. 익명 클래스 사용
        System.out.println("----- 1. 익명 클래스 사용 -----");
        getColorList(grapeList, new Function<Grape, String>() {
            @Override
            public String apply(Grape grape) {
                // TODO Auto-generated method stub
                return grape.getColor();
            }
        }).forEach(System.out::println);
        
 
        // 2. 람다식 사용
        System.out.println("----- 2. 람다식 사용 -----");
        getColorList(grapeList, grape -> grape.getColor()).forEach(System.out::println);
 
        // 3. 메소드 레퍼런스 사용
        System.out.println("----- 3. 메소드 레퍼런스 사용 -----");
        getColorList(grapeList, Grape::getColor).forEach(System.out::println);
 
    }
 
    public static class Grape {
        private Integer weight = 0;
        private String color = "";
 
        public Grape(Integer weight, String color) {
            this.weight = weight;
            this.color = color;
        }
 
        public Integer getWeight() {
            return weight;
        }
 
        public void setWeight(Integer weight) {
            this.weight = weight;
        }
 
        public String getColor() {
            return color;
        }
 
        public void setColor(String color) {
            this.color = color;
        }
 
        public String toString() {
            return "Grape{" + "color='" + color + '\'' + ", weight=" + weight + '}';
        }
    }
}
 
cs



3.5 operator 예제


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package lamdaTest;
 
import java.util.function.IntBinaryOperator;
 
public class OperatorTest {
    private static int[] valueList = { 102030 };
 
    // IntBinaryOperator 사용
    public static int maxOrMin(IntBinaryOperator operator) {
        int result = valueList[0];
        for (int value : valueList) {
            result = operator.applyAsInt(result, value);
        }
        return result; }
 
    public static void main(String[] args) {
        // 최대값 얻기
        int maxValue = maxOrMin((n1,n2) ->{
            if(n1>=n2) return n1;
            else return n2;
        });
        System.out.println(maxValue);
        
        // 최소값 얻기
        int minValue = maxOrMin((n1,n2) ->{
            if(n1<=n2) return n1;
            else return n2;
        });
        System.out.println(minValue);
    }
}
cs



정리


1. 람다식은 익명클래스를 간결하게 표현할수 있다.

2. 함수형 인터페이스는 추상 메서드 하나만 정의된 인터페이스이다.

3. 메서드 레퍼런스를 이용하면 기존의 메서드 구현을 재사용 가능하다.

4. Comparator, Predicate, Function 같은 함수형 인터페이스는 람다식을 조합할수 이는 다양한 디폴트 메소드를 제공한다.



소스코드 첨부 :


myLamdaTest.zip







서블릿 라이프사이클에 대해 궁금증을 갖게 되어 정리해보았다.



■  서블릿은 init () 메소드를 호출하여 초기화된다.


■  서블릿은 service () 메소드를 호출 하여 클라이언트의 요청을 처리한다.


■  서블릿은 destroy () 메소드를 호출하여 종료된다.


■  마지막으로, 서블릿은 JVM의 가비지 컬렉터에 의해 가비지 수집된다.



그림으로 표현하면 다음과 같다.




init 메소드는 처음 한번만 실행되고 그 이후에 service()가 다 처리를 하는 것이다.

service () 메소드는 HTTP 요청 유형 (GET, POST, PUT, DELETE 등)을 확인하고

적절하게 doGet, doPost, doPut, doDelete 등의 메소드를 호출한다.



그럼 정말로 이렇게 도는지 확인하기 위해 테스트 소스를 작성해보았다.




1. jsp 파일


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
    $(document).on("click""#button"function() {
        // get method 호출
        $.get("myServlet"function(content) {             
            $( "#list" ).append( "<li>"+content+"</li>" );
            myMethod();
        });
    
    myMethod = function(){
        // ajax post method 호출
        var myData = $("#button").val();
        $.ajax({
            type : "POST",
            data : myData,
            url : "myServlet",
            success : function(content) {
                $( "#list" ).append( "<li>"+content+"</li>" );
            }
        });
    }
});
</script>
</head>
<body>
    <button id="button" value="1">servlet 메소드 호출하기</button>
    <ul id="list">
    </ul>
</body>
</html>
 
cs



2. 서블릿 파일


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package myServletTest;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@WebServlet("/myServlet/*")
public class MyServlet extends HttpServlet{
    
    private static final long serialVersionUID = 1L;
    String msg = "default";
    
    // The servlet is initialized by calling the init() method.
    public void init() throws ServletException {
        msg = "init 메소드 Call";
    }
    
    
    // The servlet calls service() method to process a client's request.
    // service () 메소드는 HTTP 요청 유형 (GET, POST, PUT, DELETE 등)을 확인하고 
    // 적절하게 doGet, doPost, doPut, doDelete 등의 메소드를 호출
    @Override
    public void service(ServletRequest request, ServletResponse response) 
            throws ServletException, IOException {
        String text = "Service 메소드 Call";
        if(!"default".equals(msg)){
            text = msg +"<br>"+text;
            msg = "default";
        }
        response.setContentType("text/plain");  
        response.setCharacterEncoding("UTF-8"); 
        response.getWriter().write(text);       
    }
    
    
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String text = "doGet 메소드 Call";
        commonMethod(text, response);
    }
    
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String text = "doPost 메소드 Call";
        commonMethod(text, response);
    }
 
    // The servlet is terminated by calling the destroy() method.
    @Override
    public void destroy() {
        // Finalization code...
        System.out.println("destroy method call");
    }
    
 
    private void commonMethod(String text, HttpServletResponse response) 
            throws ServletException, IOException{
        if(!"default".equals(msg)){
            text = msg +"<br>"+text;
            msg = "default";
        }
        response.setContentType("text/plain");  
        response.setCharacterEncoding("UTF-8"); 
        response.getWriter().write(text);   
    }
}
 
cs


버튼을 누를시 get 요청 1번,  post 요청 1번이 이루어지고

response 시 공통되는 부분은 메소드로 빼서 작성했다.


이후 실행을 하면 


서블릿 java 파일에 service() 가 없을 경우 init() 한번 호출 이후 doGet(), doPost()를 호출한다.





서블릿 java 파일에 service() 가 있을 경우 init() 한번 호출 이후 service()를 호출한다.

service () 메소드가 HTTP 요청 유형 (GET, POST, PUT, DELETE 등)에 따라

적절하게 doGet, doPost, doPut, doDelete 등의 메소드를 호출하여 처리해줌을 알수 있었다.






이후 서블릿이 종료되면 destroy() 메소드가 발생하며 종료된다.



마지막으로 작성한 소스코드를 첨부한다.


myServletTest.zip






1. git저장소와 local git 연결(clone),  add,commit push 방법



1. github( https://github.com/ )으로 이동해 로그인 후 git저장소를 만들자. 



 





2. git저장소를 만들면 github에서 git저장소로 원격접속 가능한 주소를 제공한다.(https, ssh)



 





3. https://git-scm.com 에서 git을 다운받고 기본설정으로 설치한다.

원격 git저장소에  접속하려면 자신의 로컬컴퓨터에 git프로그램이 있어야한다.








4. 설치가 완료되면 cmd창을 열고 명령어를 입력한다. 


   git

   git --version

   (git관련 명령어와 git버전이 잘 뜬다면 설치 성공)




5. git 환경설정으로 name과 email을 지정해준다.


   git config --global user.name shlee0882

   git config --global user.email shlee0882@gmail.com




6. github에서 만든 원격 git저장소를 자신의 로컬 컴퓨터로 다운받기 위해서는

clone 명령어를 사용한다.

로컬 컴퓨터의 clone(다운로드)할 위치로 이동 후 다음 명령어로 복사(다운로드)한다.


   git clone https://github.com/shlee0882/Git-Study.git




7. 다운로드된 위치로 이동 후 text.txt파일을 하나 만든다.




8. working directory 에서 현재 상태를 확인한다. 

상태 명령어는 status이다.


   git status




9. 만든 파일을 올리기 위해서는 working directory -> staging area(index)

로 옮겨야 한다. 스태이징의 명령어는 add이다.


   git add text.txt




10. add명령이 성공적으로 되었다면 local repository(로컬 저장소)로 올려야한다.

로컬 저장소는 commit명령어를 사용하여 커밋메세지로 -m을 커밋내용을 입력한다.

   

   git commit -m "add Text File"




11. commit 명령이 성공적으로 되었다면 remote repository(원격저장소)로 올려야한다.

원격 저장소는 push명령어를 사용한다.


   git push




12. push가 완료되면 자신의 github으로 이동해서 커밋한 파일이 잘 올라갔는지 확인한다.





2. git work flow (동작원리)



앞에 명령어를 그림으로 나타내면 git은 다음과 같이 동작하는 것을 확인할 수 있다.

index는 staging하는 영역이다.


add와 commit으로 로컬 저장소까지 올릴수 있고

commit -a를 통해 add와 commit을 동시에 진행 할 수 있다.


push로 원격 저장소로 올리고

fetch로 로컬 저장소로 받을 수 있다.


fetch와 checkout으로 merge를 할수 있고

pull을 통해 fetch와 merge를 동시에 진행 할 수 있다.




 






3. git 명령어 add, reset, checkout, amend, log




sampleCode.html 을 스테이징으로 올린다.


   git add sampleCode.html




스태이징에 올라간 sampleCode.html 파일을 내려오게 한다.


   git reset sampleCode.html




sampleCode.html 파일을 원격레파지토리에 올라간것으로 바꾼다.


   git checkout -- sampleCode.html 




올라가야 할 파일을 전부 스태이징으로 올린다.


   git add . 




git 로그 확인 하기 (커밋 메세지, 해쉬 값)


   git log 




과거의 커밋한 특정 시점으로 돌아감 


   git reset --hard [hash value]

   git reset --hard adf0f596fbe1e8033e76ac2680546480fb2b0ed9





강제로 푸시 진행


   git push -f




커밋 메세지 변경


   git commit --amend




4. git branch 사용해 master에 merge해 올리기




1. 현재 브랜치 확인

   git branch





2. develop 브랜치 생성

   git brach develop





3. develop 브랜치 이동

   git checkout develop





4. 현재 브랜치 확인

   git branch





5. 파일 수정, 추가 후 push, git log 확인






현재 push한 곳은 develop 브랜치

github은 master 브랜치를 바라보고 있음

master 브랜치에도 push를 해줘야함.




6. 마스터 브랜치로 이동

   git checkout master





7. develop 브랜치와 머지

   git merge develop





8. git log 확인






HEAD -> master, develop으로

master와 develop 모두 올라간 것을 확인





9. develop 브랜치 삭제

  git branch -d develop




10. local 레파지토리에서 작업한 파일을 원격 레파지토리로 바로 올리는 방법


1
2
3
4
5
6
7
git init
git status
git add .
git commit -m "test commit"
git remote add origin https://github.com/shlee0882/git-test.git
git remote -v
git push origin master
cs



1. 객체지향 디자인 패턴


객체지향 프로그램이 복잡해지면서 이를 간결하게 정리할 필요성이 생긴 관계로 '디자인 패턴'이라는 것이 생겼다. 

디자인 패턴 프로그래밍 형식을 정하는 일종의 약속이다.


객체 지향 프로그래밍 설계를 할 때 자주 발생하는 문제들을 피하기 위해 사용되는 패턴.


여러 사람이 협업해서 개발할 때 다른 사람이 작성한 코드, 기존에 존재하는 코드를 이해하는 것은 어렵다. 

이런 코드를 수정하거나 새로운 기능을 추가해야 하는데 의도치 않은 결과나 버그를 발생시키기 쉽고 

성능을 최적화시키기도 어렵다. 이로 인해 시간과 예산이 소모된다. 


디자인 패턴은 의사소통 수단의 일종으로서 이런 문제를 해결해준다. 

예를 들어 문제 해결의 제안에 있어서도 “기능마다 별도의 클래스를 만들고, 

그 기능들로 해야할 일을 한번에 처리해주는 클래스를 만들자.”라고 제안하는 것보다 

"Facade 패턴을 써보자."라고 제안하는 쪽이 이해하기 쉽다.


일반 프로그래머가 만나는 문제가 지구상에서 유일한 문제일 확률은 거의 없다. 

이미 수많은 사람들이 부딪힌 문제다. 따라서 전문가들이 기존에 해결책을 다 마련해 놓았다. 



2. 생성 패턴(추상 객체 인스턴스화)


2.1 팩토리(Factory Method)


상위 클래스와 하위 클래스가 있을 때, 팩토리 클래스를 사용하여 하위 클래스의 인스턴스를 생성하는 패턴.


Animal.java


1
2
3
4
5
6
public class Animal {
    Animal(){
        // 생성자
    }
    // 이하 Animal의 메소드들
}
cs


Dog.java


1
2
3
4
5
6
7
8
9
10
11
12
public class Dog extends Animal{
    Dog(){
        // 생성자
    }
    public Dog(String data){
        return;
    }
    // 이하 Dog의 메소드들
    public String toString(){
        return "Dog";
    }
}
cs


Cat.java


1
2
3
4
5
6
7
8
9
10
11
12
public class Cat extends Animal{
    Cat(){
        // 생성자
    }
     public Cat(String string) {
         return;
    }
    // 이하 Cat의 메소드들
    public String toString(){
        return "Cat";
    }
}
cs


Map.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.Iterator;
import java.util.LinkedList;
 
public class Map {
    
    public static void main(String[] args){
        
        // 저장된 파일로부터 동물을 가져오는 '맵 로드' 기능을 
        // lnkList에 초기 샘플데이터 넣었다고 가정
        LinkedList<String> lnkList = new LinkedList<String>();
        lnkList.add("Dog");
        lnkList.add("Cat");
 
        // Map은 말 그대로 맵의 구현 방법에 대해서만 서술되어야 하는데, 
        // 파일을 읽는 부분에서 '동물을 분류하는' 추가적인 책임이 포함되어있다. 
        // 만약 새 동물 Rat을 넣어야 한다면 전혀 상관없는 Map 클래스를 수정해야 할 것이다.
        
        // 1. 개선 전 방법 
        Iterator<String> iter = lnkList.iterator();        
        while (iter.hasNext()) {
            if(iter.equals("Dog")){
                Dog dog = new Dog();
                System.out.println(dog);
            }else if(iter.equals("Cat")){
                Cat cat = new Cat();
                System.out.println(cat);
            }
        }
 
        // 그래서 다양한 하위 클래스들을 생성하는(Factory : 공장) 클래스를 만들어 
        // 그 클래스에 책임을 위임하는 것이다.
        // 그러면 새 클래스 AnimalFactory를 만들어보자.
        
        // 2. 개선 후 방법 
        Iterator<String> factoryIter = lnkList.iterator();    
        while (factoryIter.hasNext()) {
           Animal animal = AnimalFactory.create(factoryIter.next());
           System.out.println(animal);
        }
    }
}
cs


AnimalFactory.java


1
2
3
4
5
6
7
8
9
10
11
12
13
public class AnimalFactory {
    static Animal create(String string) {
        if(string.equals("Dog")) {
            return new Dog(string);
        } else if(string.equals("Cat")) {
            return new Cat(string);
        } else if(string.equals("Rat")){
//            return new Rat(string);
        }
       //기타 동물의 생성자들
        return null;
    }
}
cs



팩토리를 사용하면 새 동물을 추가하는지의 여부에 상관없이 

다른 클래스를 수정할 필요가 없어져 단일 책임 원칙을 잘 지키는 코드가 된다.



2.2 빌더(Builder)


빌더 클래스는 인스턴스를 생성자를 통해 직접 생성하지 않고, 빌더라는 내부 클래스를 통해 간접적으로 생성하게 하는 패턴이다.


Jacket.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Jacket {
 
    int number;
    String name;
    double size;
 
    private Jacket(int number, String name, double size) {
        //Jacket 클래스 초기화
        this.number = number;
        this.name = name;
        this.size = size;
    }
 
    // 빌더 클래스는 인스턴스를 생성자를 통해 직접 생성하지 않고, 
    // 빌더라는 내부 클래스를 통해 간접적으로 생성하게 하는 패턴이다
    public static class Builder {
        int number=0;
        String name=null;
        double size=0d;
 
        public Builder() {
            //Builder 초기화
        }
 
        public Builder setNumber(int number) {
            this.number = number;
            return this;
        }
 
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
 
        public Builder setSize(double size) {
            this.size = size;
            return this;
        }
 
        public Jacket build() {
            return new Jacket(number, name, size);
        }
    }
}
cs


JacketProduction.java


1
2
3
4
5
6
7
8
9
10
public class JacketProduction {
    public static void main(String args[]){
        createJacket();
    }
 
    public static void createJacket() {
        Jacket jacket = new Jacket.Builder().setNumber(1).setName("NorthFace").setSize(105).build();
        System.out.println(jacket.number+"  "+ jacket.name +"  "+ jacket.size);
    }
}
cs


1
1  NorthFace  105.0
cs


Builder 사용 이유


1. 클래스와 사용 대상의 결합도를 낮추기 위해


private 등의 접근 제한자로 제한하여 외부에서 임의로 접근하는 것을 막아 클래스와 사용대상의 결합도를 떨어뜨리고, 

대신 Builder라는 내부 클래스를 통해 해당 클래스를 간접적으로 생성한다.


2. 생성자에 전달하는 인수에 의미를 부여하기 위해


예를 들어서, 위에 제시된 예시에서 빌더 패턴이 없다고 가정하고 인스턴스를 생성하려면 

Jacket jacket = new Jacket(number, name, size); 이렇게 코드를 작성하여야 한다.


위의 예시에서는 인수가 세 개니까 그냥 저렇게 써도 큰 문제는 없지만, 

생성자에 전달하는 인수의 가짓수가 열 종류 가까이 되는 클래스의 경우에는 

고전적인 생성자 패턴으로는 인수를 전달하는 것이 상당히 비직관적이 된다.

(인수의 종류를 외워서 써넣어야되는 것뿐만 아니라, 인수의 순서까지 고려해야 한다!)

그래서 빌더 패턴을 통해 setXXX 형식으로 인수를 전달하면 한 눈에 보기에도 이것이 무슨 인수인지를 파악하기가 쉽다.




2.3 싱글톤(Singleton)



1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
 
    static final Singleton instance = new Singleton();
 
    private Singleton() {
        //초기화
    }
 
    public Singleton getInstance() {
        return instance;
    }
}
cs


키보드 리더, 프린터 스풀러, 점수기록표 등 클래스의 객체를 하나만 만들어야 하는 경우 사용한다. 

클래스 내에서 인스턴스가 단 하나뿐임을 보장하므로, 프로그램 전역에서 해당 클래스의 인스턴스를 바로 얻을 수 있고, 

불필요한 메모리 낭비를 최소화한다.


이 패턴에서는 생성자를 클래스 자체만 사용할 수 있도록 private 등의 접근제한자를 통하여 제한하여야 한다. 

생성자를 다른 곳에서도 사용할 수 있으면 그 곳에서도 인스턴스를 만들 수 있기 때문.


싱글톤 패턴을 사용하기 위해서는 반드시 접근제한자를 이용하여 외부의 접근을 막거나, 

final로 reference를 변경 불가능하게 설정하여야 한다. 

물론 생성자에 접근제한자를 사용하면 최소한 다른 인스턴스로 레퍼런스시키지는 못하겠지만, 

ClassName.singleton = null; 처럼 레퍼런스 자체를 지워버릴 수 있기 때문.

구현 방법에는 사전 초기화, 늦 초기화 등이 있다.


Eager initialization(사전 초기화)


클래스 로딩시에 인스턴스를 생성하는 방법이다. 위의 예시가 사전 초기화. 멀티스레드 환경에서의 

이중 객체 생성 문제가 없지만, 인스턴스를 호출하지 않아도 무조건 클래스를 초기화하기에 메모리 효율이나 연산 효율은 낮다.

Java에서는 static block initialization이라는 변종도 있다. 

클래스가 로딩될 때 최초 1회만 실행되는 static block을 통해 싱글톤 인스턴스를 초기화하는 방법인데, 구조적으로는 크게 다르지 않다.


Lazy initialization(늦 초기화)


인스턴스를 실제로 사용할 시점에서 인스턴스를 생성하는 방법이다. 세심한 방법을 쓰지 않으면 

위에 언급한 이중 객체 생성 문제가 발생할 가능성이 높으나, 

인스턴스를 실제로 사용하지 않는다면 메모리와 연산량을 아낄 수 있다는 장점이 있다.



3. 구조 패턴(객체 결합)


3.1 파사드(Facade)


파사드는 프랑스어 Façade에서 차용된 단어로 보통 건물의 출입구로 이용되는 정면 외벽 부분을 가리키는 말이다.

파사드 패턴은 시스템의 복잡성을 감추고, 사용자(Client)가 시스템에 접근할 수 있는 인터페이스(Interface)를 사용자(Client)에게 제공한다. 

따라서 파사드 패턴은 기존의 시스템에 인터페이스를 추가함으로써, 

복잡성을 감추기 위해 사용된다. 파사드 패턴은 구조적 패턴(Structural Pattern)에 포함된다.


1단계: 인터페이스를 생성한다.


1
2
3
public interface Coffee {
    void make();
}
cs



2단계: 그 인터페이스를 구현하기 위한 구체적인 클래스를 생성한다.


Americano.java


1
2
3
4
5
6
public class Americano implements Coffee {
   @Override
   public void make() {
      System.out.println("Americano::make()");
   }
}
cs


BanillaLatte.java


1
2
3
4
5
6
public class BanillaLatte implements Coffee {
   @Override
   public void make() {
      System.out.println("BanillaLatte::make()");
   }
}
cs


Cappuccino.java


1
2
3
4
5
6
public class Cappuccino implements Coffee {
   @Override
   public void make() {
      System.out.println("Cappuccino::make()");
   }
}
cs



3단계: 파사드 클래스를 생성한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CoffeeMaker {
    private Coffee americano;
    private Coffee banillaLatte;
    private Coffee cappuccino;
 
    public CoffeeMaker() {
        americano = new Americano();
        banillaLatte = new BanillaLatte();
        cappuccino = new Cappuccino();
    }
 
    public void makeAmericano(){
        americano.make();
    }
    
    public void makeBanillaLatte(){
        banillaLatte.make();
    }
 
    public void makeCappuccino(){
        cappuccino.make();
    }
}
cs



4단계: 다양한 종류의 형태를 만들기 위해 파사드를 사용한다.


1
2
3
4
5
6
7
8
9
public class FacadePatternDemo {
    public static void main(String[] args) {
        CoffeeMaker coffeeMaker = new CoffeeMaker();
 
        coffeeMaker.makeAmericano();
        coffeeMaker.makeBanillaLatte();
        coffeeMaker.makeCappuccino();
    }
}
cs



5단계: 결과값을 확인한다.


1
2
3
Americano::make()
BanillaLatte::make()
Cappuccino::make()
cs



4. 행위 패턴(객체 간 커뮤니케이션)


4.1 반복자(iterator)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
 
public class IteratorTest {
    public static void main(String args[]){
        
        // List for each
        List<Integer> list = new ArrayList<Integer>();
        list.add(1); list.add(2); list.add(3);
        
        for(Integer a : list){
            System.out.println(a);
        }
        
        // Iterator
        LinkedList<String> lnkList = new LinkedList<String>();
        lnkList.add("Dog"); lnkList.add("Cat");
        
        Iterator<String> iter = lnkList.iterator();
        
        while(iter.hasNext() == true) {
          Object object = iter.next();
          System.out.println(object);
        }
    }
}
cs


1
2
3
4
5
1
2
3
Dog
Cat
cs



5. 기타


5.1 MVC


최근 애플리케이션 개발에 있어 상당히 중요한 모델이 된 패턴으로 Model, View, Controller 세 가지 부분으로 이루어져 있다. 

Model은 자료(Data)를 생성, 저장, 처리하는 역할을 하는 부분이다. 

View는 Model로부터 받은 자료를 여러 가지 형태로 사용자에게 보여주는 역할을 한다. 

Controller란 소프트웨어의 흐름을 제어하는 것으로 View와 Model 사이에서 관계를 설정하여 주는 부분을 말한다. 

Controller는 Model이나 View가 바뀌더라도 수정 없이 작동되어야 한다.



테스트 작성한 소스 첨부


myTest5.zip




+ Recent posts