SOLID

J/Java 2017. 6. 12. 18:00
  1. 단일 책임 원칙 (Single responsibility principle)

    클래스는 단 한개의 책임을 가져야 한다. 
    클래스 변경은 단 하나여야만 한다.

    단일 책임 원칙
    public class DataViewer {
     
       public void display(){
          String data = loadHtml();
          updateGui(data);
       }
     
        //
        public String loadHtml(){
            HttpClient client = new HttpClient();
            client.connect(url);
            return client.getResponse();
        }
     
       private void updateGui(String data){
          GuiData guiModel = parseDataToGuiData(data);
    //    tableUI.changeData(guiModel);
       }
     
       private GuiData parseDataToGuiData(String data) {
          return null;
       }
    }

    DataViewer에서 데이터 화면을 보여주는 display, updateGui, parseDataToGuiData 와 Html을 로드하는 loadHtml 메소드가 있다. DataViewer이라는 클래스에 화면을 보여주는 책임을 갖고 있지만 loadHtml이라는 html을 로드하는 부분도 포함되어있어 Single responsibility principle에 어긋나게 된다.
     

    단일 책임 원칙 DataViewer - DataLoader 분리
    public class DataViewer {
       DataLoader<String> dataLoader;
     
       public void display(){
          String data = dataLoader.loadHtml();
          updateGui(data);
       }
     
       private void updateGui(String data){
          GuiData guiModel = parseDataToGuiData(data);
    //    tableUI.changeData(guiModel);
       }
     
       private GuiData parseDataToGuiData(String data) {
          return null;
       }
    }
     
     
    public class DataLoader<T> {
     
       public T loadHtml() {
          HttpClient<T> client = new HttpClient<T>();
     
          client.connect();
          return client.getResponse();
       }
     
       class HttpClient<T> {
          private T response;
     
          public void connect() {
     
          }
     
          public T getResponse() {
             return response;
          }
       }
     
    }

    따라서 DataLoader 클래스를 따로 두어 분리한다. 


  2. 개방 폐쇄 원칙 (Open-closed principle)
    확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
    기능을 변경하거나 확장할 수 있으면서 그 기능을 사용하는 코드는 수정하지 않는다.


  3. 리스코프 치환 원칙 (Liskov substitution principle)

    상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
    파생 클래스는 부모 클래스와 치환되더라도 문제가 없어야 한다.
    부모 클래스가 제공하는 메소드 형태를 따라 기능을 제공해야 한다.
    파생 클래스는 확장이 주요 목적이다. 추가는 부차적인 문제이다.

    ex) Rectangular, Square 상속 관계

  4. 인터페이스 분리 원칙(Interface segregation principle)

    인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.

    ex) Java Swing의 JTable, Android Activity의 View?

  5. 의존 역전 원칙(Dependency inversion principle)

    고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 된다. 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야한다.


설정

트랙백

댓글

자바스크립트 [1,2,3,[1,2,3],[1,2,3],[[[1]]]] 이런 문자열을 리스트로 바꾸고 싶은 경우가 발생한다. 이때 아래 예시 처럼 processBracket 처리 후, toList로 변환하면 된다.

package demo; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Stack; import org.junit.Test; public class DemoApplicationTests {

@Test public void test1() { // javascript 배열 String str = "[1,[2,[1],1]]"; // 대각선 depth 위치 파악 배열 int[] strBracket = new int[str.length()]; procBracket(str, strBracket); List list = (List) toList(str, strBracket, 0, str.length() - 1); printList(list, 0); } // depth 0, 1, 2, 3, 4 .... // array [] [] [] [] public Object toList(String str, int[] strBracket, int sIdx, int lIdx) { List res = new ArrayList(); boolean isFirst = true; int cIdx = sIdx; for (;;) { int listIdx = str.indexOf('[', cIdx); int commaIdx = str.indexOf(',', cIdx + 1); // depth가 증가하는지 여부를 파악 if (listIdx != -1 && commaIdx != -1 && listIdx < commaIdx) cIdx = listIdx; else if (listIdx != -1 && commaIdx == -1) cIdx = listIdx; // 대각선이 있을 경우 depth를 증가시켜 해당 구역에 대한 toList를 구한다. if (str.charAt(cIdx) == '[') { int nextIdx = strBracket[cIdx]; // System.out.println((cIdx + 1) + " - " + (nextIdx - 1)); res.add(toList(str, strBracket, cIdx + 1, nextIdx - 1)); cIdx = nextIdx; if (isFirst) isFirst = false; } else if (isFirst || str.charAt(cIdx) == ',') { // 처음 일 경우 ,가 맨 앞에 존재하지 않아 예외처리로 따로 -1을 더한다. if (isFirst) cIdx -= 1; int nextIdx = str.indexOf(',', cIdx + 1); // 끝에 도달했을 경우 if (nextIdx > lIdx || nextIdx == -1) nextIdx = lIdx + 1; // System.out.println(str.substring(cIdx + 1, nextIdx).trim()); res.add(str.substring(cIdx + 1, nextIdx).trim()); if (isFirst) isFirst = false; cIdx = nextIdx; } // 다음 ,위치로 이동시킨다. int nextIdx = str.indexOf(',', cIdx); // 끝에 도달했을 경우, 끝냄. if (nextIdx > lIdx || nextIdx == -1) break; cIdx = nextIdx; } // System.out.println("finish"); return res; } }


'J > Java' 카테고리의 다른 글

SOLID  (0) 2017.06.12
자바 유용한 라이브러리  (0) 2014.12.11
Java 내부 분석  (0) 2014.10.12
자바 외부 멤버변수를 내부 클래스 안에서 호출 할 경우, 주의사항  (0) 2014.10.09
jackson generic type 관련  (0) 2014.08.24

설정

트랙백

댓글

Lombok 

써봤는 데, 의외로 유용하였다. 

get, set 메소드를 줄이는 것도 있지만 해쉬, toString을 동시에 만든다는 점에서 괜찮은 라이브러리이다.


Guava

구글에서 만든 라이브러리

사용은 안해봤지만 괜찮은 듯



'J > Java' 카테고리의 다른 글

SOLID  (0) 2017.06.12
javascript 배열 java 리스트 변환  (0) 2015.06.03
Java 내부 분석  (0) 2014.10.12
자바 외부 멤버변수를 내부 클래스 안에서 호출 할 경우, 주의사항  (0) 2014.10.09
jackson generic type 관련  (0) 2014.08.24

설정

트랙백

댓글

Java 내부 분석

J/Java 2014. 10. 12. 01:46

Java 내부 분석


VisualVM, jconsole ..기타등등의 메모리,  CPU 말그대로 내부적인 전반적인 것들을 보여준다.


이것들을 어디다 쓰느냐 하면 예로들면, Memory Leak 현상이 발생하거나 프로그램 사이즈가 커졌을 때가 있는 데,


나 같은 경우에는 메모리 릭 현상이 발생하여 사용하게 되었다.


스프링 프레임워크를 사용하면서 간과했던 실수인데 static 선언을 했을 시, 메모리가 누적되어 쌓이는 증상이 발생할 수 있다.


멀티 스레드 환경에선 static으로 선언된 부분이 classloader에 계속 올라오는 현상이 생기는 거 같다.


결론은 메모리 누적이 발생하면 jconsole이나 VisualVM을 이용해서 내부 분석을 하여 해당 누적 클래스를 찾는 것이 중요하다.


자세한 내용은 구글 검색하면 나옴니다. :)


설정

트랙백

댓글

package com.example.e03list;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity {

	LectureDBOpenHelper helper;
	SQLiteDatabase db;
	Cursor cursor;
	SimpleCursorAdapter adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		adapter = new SimpleCursorAdapter(this, R.layout.list_item, null,
				new String[] { "title", "modifiedTime", "_id" }, new int[] {
						R.id.text_view1, R.id.text_view2, R.id.text_view3 },
				CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
		ListView listView = (ListView) findViewById(R.id.listView1);
		listView.setAdapter(adapter);

		Button b = (Button) findViewById(R.id.button1);
		View.OnClickListener onClick = new OnClickListener() {
			public SQLiteDatabase db1 = db;
			
			@Override
			public void onClick(View v) {
				Log.e("db1", "db1 : " + db1 + " db : " + db); 
				// db1은 대입하여 메소드 호출하는 식
				// db는 내부 익명 클래스에서 외부 멤버변수를 호출하는 형식이라 
				// resume때 생성해도 상관이 없다.

				EditText e = (EditText) findViewById(R.id.editText1);
				String str = e.getText().toString();
				e.setText("");
				ContentValues value = new ContentValues(1);
				value.put("title", str);
				db.insert("ListItem", null, value);
				cursor.close();
				cursor = db.rawQuery("select * from ListItem", null);
				adapter.changeCursor(cursor);
			}
		};
		b.setOnClickListener(onClick);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onResume() {
		super.onResume();
		helper = new LectureDBOpenHelper(this);
		db = helper.getWritableDatabase();
		cursor = db.rawQuery("SELECT * FROM ListItem", null);
		adapter.changeCursor(cursor);
	}

    @Override
    protected void onPause() {
        super.onPause();
        cursor.close(); 
        db.close();
        helper.close();
    }
}

'J > Java' 카테고리의 다른 글

자바 유용한 라이브러리  (0) 2014.12.11
Java 내부 분석  (0) 2014.10.12
jackson generic type 관련  (0) 2014.08.24
URLConnection을 이용한 이미지 추출 프로그램  (0) 2014.06.28
Static 키워드  (0) 2014.03.18

설정

트랙백

댓글

jackson generic type 관련

J/Java 2014. 8. 24. 16:59
http://www.javacodegeeks.com/2013/01/json-deserialization-with-jackson-and-super-type-tokens.html

 

설정

트랙백

댓글

 

ExportImg.java

 

URLConnection을 이용한 이미지 추출 프로그램

 

해당 URL을 입력하면 URL 페이지 창에 있는 이미지들을 지정한 폴더에 추출하는 프로그램이다.

- 테스트 해 본 곳 : 페이스 북, 구글 ( 구글은 안됨 )

'J > Java' 카테고리의 다른 글

자바 외부 멤버변수를 내부 클래스 안에서 호출 할 경우, 주의사항  (0) 2014.10.09
jackson generic type 관련  (0) 2014.08.24
Static 키워드  (0) 2014.03.18
자바 제너릭  (1) 2014.03.16
final 키워드의 중요성  (0) 2014.03.16

설정

트랙백

댓글

Static 키워드

J/Java 2014. 3. 18. 12:42

1. 클래스를 설계 할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야 하는 것에 static을 붙인다.

- 인스턴스를 생성하면, 각 인스턴스들은 서로 독립적이기 때문에 서로 다른 값을 유지한다.

경우에 따라서는 각 인스턴스들이 공통적으로 같은 유지되어야 하느 경우 static을 붙인다.

2. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.

- static이 붙은 멤버젼수(클래스 변수)는 클래스가 메모리에 올라갈 때 이외 자동적으로 생성되기 때문이다.

3. static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.

- static이 메서드는 인스턴스 생성 없이 호출 가능한 반면, 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문에...

static이 붙은 메서드(클래스 메서드)를 호출할 때 인스턴스가 생성되어 있을 수도 그렇지 않을 수도 있어서 static이 붙은 메서드에서 인스턴스 변수의 사용을 허용하지 않는다.

(반대로, 인스턴스 변수나 인스턴스 메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다.

인스턴스 변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.)

4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.

- 메서드의 작업 내용 중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없다.

반대로 인스턴스 변수를 필요로 하지 않는다면, 가능하면 static을 붙이는 것이 좋다.

메서드 호출 시간이 짧아지기 때문에 효율이 높아진다.

(static을 안 붙인 메서드는 실행시 호출되어야 할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.)

5. 클래스 설계 시 static의 사용 지침

- 먼저 클래스의 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는 지 살펴보고 있으면, static을 붙여준다.

- 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을 붙일 것을 고려한다.

 

추가 사항 정적 내부 클래스 예제

...// staticㅡㅇ로 선언된 변수만 정적 내부 클래스 안에서 쓸 수 있다.

static class StaticNestedClass{ // 정적 내부 클래스 ... }

class Inner{ // 비정적 내부 클래스 }

정적 내부 클래스 쓰기 위한 객체 생성

outer.StaticNestedClass n = new Outer.StaticNestedClass(); // outer 밖에서 생성

StaticNestedClass n = new StaticNestedClass(); // outer 안에서 생성

 

// 예제

class Outer{

staic int a = 1;

int b = 2;

static void f(){

System.out.println("I'm f()");

}

void a(){

System.out.println("I'm g()");

}

static class StaticNestedClass{

int c = 11;

static int d = 22;

 

void h(){

System.out.println("나는 정적 내부 클래스다.");

System.out.println(a);

// System.out.println(b); static변수가 아니므로 에러

Outer o = new Outer();

System.out.println(o.b); // 객체 생성해서 쓰면 된다.

System.out.println(c);

System.out.println(d); // 멤버 변수가 static이라도 상관없다.

System.out.println(this.d); // static의 영향이 미치지 않음.

// System.out.println(Outer.this.a); // Outer 멤버 참조 시 this 사용불가

// System.out.println(Outer.this.b);

f();

// g(); static 메소드가 아니므로 에러

static void i(){

System.out.println("I'm i()");

// System.out.println(this.a); // static 메소드에서 this 사용 불가

}

}

}

}

class Test {

public static void main(String[] args){

Outer.StaticNestedClass n = new Outer.StaticNestedClass(); // Outer 객체 없이 생성 가능

n.h();

}

설정

트랙백

댓글

자바 제너릭

J/Java 2014. 3. 16. 22:14

public static <T, U> T[] copyOf()의 해석

아래의 소스는 java.util.Arrays 클래스의 메소드 중 하나.

<T, U> T[]의 의미는 무엇인가? T라는 클래스가 존재한다면 무슨 용도인가?

 

제너릭의 정의

JDK 5.0에서는 GENERIC이 포함되어 실행전 컴파일 단계에서 특정 Collection에 프로그래머가 원하는 객체 타입을 명시하여 명시된 객체가 아니면 절대 저장이 안되게 할 수 있습니다.

Generic Type은 꺽쇠(<>) 사이에 컴파일 할 당시 사용될 객체를 적어 놓으면 객체를 저장할 때 제너릭 타입으로 저장됩니다.

API에서 전달되는 객체가 현 객체 내에서 하나의 자료형으로 쓰일 때는 <T>로 유도하며 전달되는 객체가 현 객체 내에서 하나의 요소로 자리 잡을 때는 <E>, 그리고 Key값으로 사용될 때는 <K>로, Value값으로 사용 될 때는 <V>로 표현하고 있습니다.

 

ex) public class GenericClass<T>{

T[] v;

public void set(T[] n){ v = n; }

public void print(){ for(T s : v){ out.println(s); }}

}

 

public class GenericTest{

public static void main(String[] args){

GenericClass<String> t = new GenericClass<String>();

String[] s = {"가", "나", "다"};

t.set(s);

t.print();

}

}

 

public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType){

T[] copy = ((Object)newType == (Object)Object[].class)?

(T[])new Object[newLength] : (T[])Array.newInstance(newType.getComponentType(), newLength);

System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));

return copy;

}

 

제너릭스의 범위 설정

Generic

 

제네릭스 <E>는 모든 형태를 받을 수 있지만, 의도적으로 상속 관계를 기준으로 들어올 수 있는 클래스 범위를 지정 할 수 있다.

super는 하한선이고

extends는 상한선이다.

<T extends Comparable<T>>이와 같이 제네릭스의 영역을 설정하면

T로 들어올 수 있는 값은 extends(하한선)에 의해

Comparable<T> 클래스이거나 그 자식(상속받은) 클래스이어야 한다.

<T super Vector> 이와 같은 제네릭스를 설정하면

T로 들어올 수 있는 값은 super (상한선)에 의해 Vector 클래스이거나 그 조상 클래스이어야 한다.

(조상 클래스가 들어왔는데 Vector가 조상클래스의 자손인지 확인하는 방법은 아마 instanceof )

 

이와 같이 범위 제한은, 클래스 앞 부분 혹은 메소드 앞 부분에 정의한다.

// 메소드 앞부분에 정의

static <T extends Comparable<T>> void swap(T[] array, int a, int b){

T temp = array[a]; array[a] = array[b]; array[b] = temp;

}

 

// 클래스 앞 부분 정의

class Demo<T super Study>

 

private A[] element  = A[aSize]; 라고 선언

그러나 5.0 이상에서는 "generic array creation" 이라는 에러로 처리.

이유는 자바 소드 코드의 제네릭 자료형이 컴파일 시 까지만 존재하고 실제 컴파일 된 바이트 코드에는 존재하지 않기 때문에 실행시간에 해당하는 제네릭 자료형의 인스턴스를 만드는 것을 원칙적으로 불가능.

그래서 제네릭 자료형의 배열도 생성할 수 없다.

 

제네릭스 와일드 카드 ?라는 말에서 알 수 있듯이 어떠한 값도 맏을 수 있는 제네릭 자료형을 의미한다.

와일드 카드는 "?" 문자로 표시.

예를 들면 List<?> list  = new ArrayList<String>(); 에서 list 인스턴스 변수는 String 값과 Integer 값 모두를 받을 수 있다.

상속 계층 구조상의 경계도 지정이 가능하단다. 이를 위해 super, extends 라는 두 예약어를 사용한다.

 

List<? extends Number> List<? super Integer>

extends를 사용하는 와일드 카드는 흔히 와일드 카드의 하한을 지정한다고 하는 데.

그 말은 extends 다음에 나오는 클래스를 포함하여 그 자식 클래스들이 제네릭 자료형으로 올 수 있음을 나타낸다.

super를 사용하는 와일드 카드는 그 반대로 와일드 카드의 상한을 지정하는 데, 그의미는 super 다음에 나오는 클래스를 포함하여 그 부모 클래스들이 제네릭 자료형으로 올 수 있음을 나타낸다.

 

 

설정

트랙백

댓글

자바 프로그래밍을 할 때 사람들을 괴롭히는 한 가지 주제는 '메모리'이다.

관련된 주제는 가비지 컬렉션, Single 패턴 등이 있을 테지만 흔히 간과하는 것 중 하나가 final 키워드의 사용이다.

1. final 키워드는 변수, 메소드의 선언에 쓰인다.

2. final 키워드를 사용한 변수는 상속이 안된다.

3. final 키워드로 할당한 변수를 변경할 수 없다.

4. final 키워드를 사용한 변수는 객체의 생성 당시에 메모리에 값이 할당되어야 한다.

 

변수가 한번만 선언된다는 것이 왜 중요한 것인지 이해하려면 자바에서 다루는 메모리 시스템에 대한 이해가 선행되어야 한다.

우선 변수를 하나 만들어 보자. String _a = "Hi";

변수 _a는 "Hi" 라는 값을 가리키고 있다. 값을 저장하는 것이 아니라 '가리키는(reference)' 것이다.

이 행동은 쉽게 다른것들에 의해 복제 될 수 있다.

String _b = "Hi"; 이제 _b도 "Hi"를 가리키고 있다. 또 다른 경우를 생각해보라.

SomeObject obj = new SomeObject(); obj 객체를 생성했다.

좌측은 obj 객체의 주소를 저장할 공간을 생성한 것이고, 오른편은 구체적인 값을 저장한 공간을 지정해 둔 것이다.

이들을 등호로 할당하여 값과 주소를 연결했다.

한편 시스템이 아주 복잡하게 움직인다고 하자(예를 들어 쓰레드가 1000개쯤) 우연치 않게 컴퓨터는 이런 말도 안되는 생각을 할 수 있다. '아... 값을 저장할 곳이 없네.'

우리도 도서관에서 누군가 책만 샇아두고 쓰지 않으면 그책을 치워버리고 그 자리에 앉고 싶어진다.

자바가상머신(컴퓨터)도 그러지 말라는 법이 없다.

SomeObject obj의 주소 값은 그대로 살아있겠지만, obj가 가리키는 저장소가 사용되지 않는 다고 판단된다면 이 저장소를 초기화시켜 버린다. 그러면 더 이상 obj는 값을 가리키지 못하는 상태(한편, null을 가리킨다고 봐도됨)가 된다.

그러면 이와같은 웃긴 일도 벌어질 수 있다. 자바프로그램의 쓰레드들이 축구를 하다가 갑자기 공이 사라지는 것이다. 왜냐하면 선수들이 전반전을 끝내고 휴식하는 사이, 축구공을 쓰지 않는다고 판단한 누군가가 공을 가져가 버렸기 때문이다.

어떻게 막을까?

final 키워드를 붙여서 가리키는 주소와 값을 본드로 딱 붙여버리면 끝이다.

final SomeObject obj = new SomeObject();

어떤 일이 벌어지는고 하니 메모리를 생성한 후, 이 메모리를 가리키는 주소 값은 유일하게 obj가 관리한다.

다른 주소값을 가진 객체가 값을 저장하는 저장소에 접근하려고 하면 이미 obj가 버티고 있어서 막을 것이다.

결정적으로 이 obj 변수를 가지고 있는 객체가 가비지컬렉터에 의해 사라지기 전까지 obj가 가리키고 있는 저장소는 누구도 쓸 수 없다.

즉, obj가 사망해야 저장소도 풀려난다. 앞서 비유를 생각하면 축구가 끝나야 공이 다른 곳에 쓰일 수 있다.

 

 

설정

트랙백

댓글