소프트웨어공학 Coding
Introduction The interaction diagrams and DCDs are used as input to the code generation process A B :A :B :C C Design Class Diagram Interaction Diagram Code Generation Object code Implementation in an object-oriented language requires writing code for Class and interface definitions Method definitions
Mapping Designs to Code Defining a class with methods and simple attributes SalesLineItem quantity: Integer getsubtotal(): Money Described-by * 1 ProductSpecification public class SalesLineItem { private int quantity; public SalesLineItem(ProductSpecfiication spec, int qty) { public Money getsubtotal() {
Mapping Designs to Code Creating Methods from Interaction Diagrams public class Register { private ProductCatalog catalog; private Sale sale; public void enteritem(itemid id, int qty) { ProductSpecification spec = catalog.getspecification(id); sale.makelineitem(spec,qty);. enteritem(id,qty) :Register 2:makeLineItem(spec,qty) sale:sale 1:spec:=getSpecification(id) catalog:productcatalog
Mapping Designs to Code Mapping Attributes In some cases one must consider the mapping of attributes from the design to the code in different languages. In Java, the java.util.date class combines both date and timestamp information. Therefore, the separate attributes in the design can be collapsed when mapping to Java. public class Sale { private Date datetime = new Date(); Sale date:date iscomplete:boolean time:time becomecomplete() makelineitem( ) makepayment( ) gettotal()
Mapping Designs to Code Adding reference attributes A reference attribute of a class are suggested by the associations and navigability SalesLineItem quantity: Integer getsubtotal(): Money * Described-by 1 productspec ProductSpecification Simple Attribute Reference Attribute public class SalesLineItem { private int quantity; private ProductSpecification productspec; public SalesLineItem(ProductSpecfiication spec, int qty) { public Money getsubtotal() {
Mapping Designs to Code Collection Classes in Code One-to-many relationships is implemented with the introduction of a collection object, such as a List or Map. Sale iscomplete:boolean time:time becomecomplete() makelineitem( ) makepayment( ) gettotal() lineitems 1..* SalesLineItem quantity: Integer getsubtotal(): Money public class Sale {. private List lineitems = new ArrayList();
Mapping Designs to Code Order of Implementation Classes need 7to be implemented from least-coupled to mostcoupled 3 2 6 5 4 1
리팩토링 리팩토링 (Refactoring) 이란? 외부적으로나타나는기존코드의행위를변경하지않고, 내부를재작성하거나재구성하는체계적이고원칙있는기법 리팩토링의활동과목표는? 코드의중복제거 명확성향상 긴메소드를짧게만들기 하드코드된문자열상수의제거 기타등등 잘리팩토링된코드는마스터프로그래머가수행한것처럼짧고, 응집도가높으며, 명확하고, 중복이없다.
Eclipse 를활용한리팩토링 Eclipse 의리팩토링툴은크게세가지범주로나눌수있다.( 이것은 Refactoring 메뉴에나타나는순서이다 ): 1. 물리적재구성 (reorganization) 과재명명 (renaming) 필드, 변수, 클래스, 인터페이스의이름을변경하고, 패키지와클래스옮기기 2. 클래스관계재정의 클래스레벨에서코드의논리적조직을변경 익명의클래스들을중첩클래스로옮기며, 중첩클래스들을상위레벨클래스로전환하고 구체클래스에서인터페이스를만들고 클래스에서하위클래스또는상위클래스로메소드또는필드옮기기 3. 클래스내에있는코드변경 로컬변수를클래스필드로변환 메소드내의선택된코드를개별메소드로전환 getter 와 setter 메소드생성하기
물리적재구성 (reorganization) 과재명명 (renaming) 특별한툴없이파일시스템에있는파일들을재명명하거나옮길수있다. 하지만자바소스파일로이일을수행하려면 import 또는 package 문을업데이트하기위해많은파일들을편집해야한다. 텍스트에디터의검색을사용하여클래스, 메소드, 변수를쉽게재명명할수있고기능을대체시킬수있다. 하지만다양한클래스들이같은이름을가진메소드또는변수를갖기때문에조심해야한다. 모든인스턴스가정확히구분되고변경되도록확인해야한다. Eclipse 의 Rename 과 Move 는사용자가개입하지않고이러한변경들을지능적으로수행할수있다. Eclipse 는코드문법을이해하고특정메소드, 변수, 클래스이름에대한레퍼런스를구분할수있기때문이다.
Eclipse 에서의 Rename 자바엘리먼트를 Rename 하려면 Package Explorer 뷰에있는 rename 을클릭하거나자바소스파일에서이를선택한다음 Refactor > Rename 을선택한다.
Eclipse 에서의 Rename 새로운이름을명명하고 Eclipse 가이이름에대한레퍼런스도변경하는지의여부를확인한다. [Open Rename Dialog..] 를선택하고, getter 와 setter 메소드를가진필드를선택하면이새로운필드를반영하기위해이메소드들의이름을업데이트할수있다.
Eclipse 에서의 Move Move 는 Rename 과비슷하다 : 자바엘리먼트 ( 대게, 클래스 ) 를선택하고이것의새로운위치를지정하고레퍼런스의업데이트여부를확인한다. 리팩토링수행전에 Preview 를선택해변경사항을검토하거나 OK 를눌러리팩토링을수행할수있다.
클래스관계재정의 Eclipse 의리팩토링세트를이용하면클래스관계를자동으로변경할수있다. 이러한리팩토링은 Eclipse 가제공하는다른유형의리팩토링만큼일반적인것은아니지만매우복잡한태스크를수행한다는점에서가치가있다 Convert Anonymous Class to Nested Move Type to New File 클래스계층내에서멤버옮기기
Convert Anonymous Class to Nested 익명의클래스가너무커져서코드판독이불가능하게되면익명의클래스를정식클래스로만드는것을고려해봐야한다. 캡슐을유지하려면상위레벨클래스보다는중첩클래스로만들어야한다. 익명클래스안에서클릭하여 Refactor > Convert Anonymous Class to Nested 를선택한다. 클래스이름을입력하고 Preview 또는 OK 를선택한다 interface Bag { public void set(object msg); public Object get(); public class BagExample { void processmessage(string msg) { Bag bag = new Bag() { Object o; public Object get() { return o; public void set(object o) { this.o = o; ; bag.set(msg); public class BagExample { private final class BagImpl implements Bag{ Object o; public Object get() { return o; public void set(object o) { this.o = o; void processmessage(string msg) { Bag bag = new BagImpl(); bag.set(msg);
Move Type to New File 다른클래스에도사용가능한중첩클래스를만들때유용하다. 소스파일에서클래스이름을강조하고 ( 또는아웃라인뷰의클래스이름을클릭하여 ) Refactor > Move Type to New File 을선택한다. public class BagExample { private final class BagImpl implements Bag{ Object o; public Object get() { return o; public void set(object o) { this.o = o; void processmessage(string msg) { Bag bag = new BagImpl(); bag.set(msg); MessagePipe pipe = new MessagePipe(); pipe.send(bag); public class BagExample { void processmessage(string msg) { Bag bag = new BagImpl(); bag.set(msg); final class BagImpl implements Bag{ Object o; public Object get() { return o; public void set(object o) { this.o = o;
클래스계층내에서멤버옮기기 Push Down & Pull Up 리팩토링은클래스메소드또는필드를하나의클래스에서이것의하위클래스또는상위클래스로각각이동시킨다. 추상클래스 Vehicle 과 Automobile 이라는 Vehicle 의하위클래스가있다고가정해보자. public abstract class Vehicle { protected int passengers; protected String motor; public int getpassengers() { return passengers; public void setpassengers(int i) { passengers = i; public String getmotor() { return motor; public void setmotor(string string) { motor = string; public class Automobile extends Vehicle { private String make; private String model; public String getmake() { return make; public String getmodel() { return model; public void setmake(string string) { make = string; public void setmodel(string string) { model = string;
클래스계층내에서멤버옮기기 Vehicle 의애트리뷰트중하나가 motor 라는것에주목하자. 모터로구동되는탈것만을다룰것이라면괜찮지만보트같은것도허용하려면 motor 애트리뷰트를 Vehicle 클래스에서 Automobile 클래스로밀어내려야한다. motor 를선택하고 Refactor > Push Down 을선택한다
클래스계층내에서멤버옮기기 Eclipse 는언제나정확하게작동하는것은아니다. 이필드에근거하는모든메소드가 "push down" 되었는지를확인해야한다. 이경우 motor 필드를수반하는 getter 와 setter 메소드도같이리펙토링될수있도록하여야한다.
클래스계층내에서멤버옮기기 Pull Up 리팩토링은 Push down 과거의동일하다. 단클래스멤버를클래스에서상위클래스로옮긴다 Automobile 클래스에 motor가있는경우에, Bus 같이 Vehicle의다른하위클래스를만든다면 motor를 Bus 클래스에도추가해야한다. 이와같은관계를나타내는한가지방법은 Motorized라는인터페이스를만들어, Automobile과 Bus가구현하도록한다. 단 RowBoat는구현하지않는다. Motorized 인터페이스를만드는가장쉬운방법은 Automobile 에 Extract 인터페이스리팩토링을사용하는것이다.
Pull Up 리팩토링예 Automobile 클래스를선택하고메뉴에서 Refactor > Extract Interface 를선택한다
클래스내부에서코드변경하기 가장광범위한리팩토링은클래스내에서코드를인식하는리팩토링이다. 무엇보다도이들은중간변수를가져오고, 메소드의한부분에서새메소드를만들고필드용 setter 및 getter를만든다. Extracting & inlining Extract Method Extract Local Variable Extract Constants
클래스내부에서코드변경하기 (Extract Method) import java.util.properties; import java.util.stringtokenizer; public class StartApp { public static void main(string[] args) { Properties props = new Properties(); for (int i= 0; i < args.length; i++) { if(args[i].startswith("-d")) { String s = args[i].substring(2); StringTokenizer st = new StringTokenizer(s, "="); if(st.counttokens() == 2) { props.setproperty(st.nexttoken(), st.nexttoken()); //continue... 이부분의코드가다른곳에서도재사용될수있다면, 이부분을메소드로추출할필요가있다.
클래스내부에서코드변경하기 이를수행하기위해서는이코드를강조하고그다음 Refactor > Extract Method 를메뉴에서선택한다. 메소드이름으로 addproperty 를입력하고이메소드가두개의매개변수 (Properties prop, String s) 를갖고있음을확인한다
리팩토링후결과 import java.util.properties; import java.util.stringtokenizer; public class StartApp { public static void main(string[] args) { Properties props = new Properties(); for (int i = 0; i < args.length; i++) { if (args[i].startswith("-d")) { String s = args[i].substring(2); addproperty(props, s); // continue... private static void addproperty(properties props, String s) { StringTokenizer st = new StringTokenizer(s, "="); if (st.counttokens() == 2) { props.setproperty(st.nexttoken(), st.nexttoken());
Extract Local Variable Extract Local Variable 리팩토링은로컬변수에할당될익스프레션을강조한후에, Refactor > Extract Local Variable 을메뉴에서선택한다 변수를제공하라는프롬프트가뜨면 key 를입력한다. 선택된모든익스프레션을새로운변수에대한레퍼런스로대체하는옵션이있다. 이것은종종적절하지만 nexttoken() 메소드의경우에는그렇지않다. 호출될때마다매번다른값을리턴한다. 이옵션에는선택되지않았다
Extract Local Variable 그런다음 st.nexttoken() 에대한두번째호출에대해이리팩토링을반복한다. 이번에는새로운로컬변수 value 를호출한다. 다음코드는두개의리팩토링후의코드모습이다. private static void addprop(properties props, String s) { StringTokenizer st = new StringTokenizer(s, "="); if (st.counttokens() == 2) { String key = st.nexttoken(); String nexttoken = st.nexttoken(); props.setproperty(key, nexttoken); 이러한방식으로변수를도입할때여러이점이있다. 우선, expression 에의미가풍부한이름을제공함으로써코드가무엇을수행하는지가분명해진다. 둘째로, expression 이리턴하는값을쉽게검사할수있기때문에코드디버깅이더욱쉽다. 마지막으로 expression 의많은인스턴스들이하나의변수로대체될수있는경우더욱효율적이다.
Extract Constant Extract Constant 는 Extract Local Variable 와비슷하지만정적상수 Expression 을선택해야한다. 이것은하드코딩된숫자와스트링을코드에서제거하는데유용하다. 예를들어, 앞의코드에서이름값쌍을정의하면서명령행옵션을위해 -D 를사용했다. 코드에서 -D 를하이라이트하고 Refactor > Extract Constant 를선택한다음상수이름으로 DEFINE 을입력한다. 이리팩토링은코드를변경할것이다. if (args[i].startswith("-d")) { String s = args[i].substring(2); addproperty(props, s); if (args[i].startswith(define)) { String s = args[i].substring(2); addproperty(props, s);
필드의캡슐화 객체의내부구조를노출하는것은좋은관례로여겨지지않았다. Vehicle 클래스와이것의하위클래스가프라이빗필드또는보호필드를갖고있고퍼블릭 setter 와 getter 메소드가액세스를제공하는이유도그것이다. 이러한메소드들은다음두가지다른방법으로만들어진다. Source > Generate Getter and Setter 를사용하는방법 각각의필드에대한 getter 와 setter 메소드를정의한다이얼로그박스가디스플레이될것이다. 이것은리팩토링은아니다. 새로운메소드를사용하기위해필드에대한레퍼런스를업데이트하지않기때문이다. 이방법은시간절약에는효과가있지만클래스를처음만들때가장적합하다. 또는새로운필드를클래스에추가할때도적절하다. 필드를선택하고메뉴에서 Refactor > Encapsulate Field 를선택하는방법 한번에한필드에대한 getter 와 setter 를만들지만윗방법과는달리필드에대한레퍼런스를새로운메소드에대한호출로변경한다.
필드의캡슐화 새로운 Automobile 클래스로시작해보자 public class Automobile { public String make; public String model; Automobile 을인스턴스화하는클래스를만들고 make 필드에직접액세스한다. public class AutomobileTest { public void race() { Automobile car1 = new Automobile(); car1.make= "Austin Healy"; car1.model= "Sprite"; //...
필드의캡슐화 필드이름을강조하고 Refactor > Encapsulate Field 를선택하여 make 필드를캡슐화한다. 다이얼로그에서 getter 와 setter 메소드용이름을입력한다. 예상했겠지만이는기본적으로 getmake() 와 setmake() 이다. 필드와같은클래스에있는메소드가필드에직접액세스할것인지또는이레퍼런스가변경되어모든다른클래스처럼액세스메소드를사용할것인지를선택한다.
필드의캡슐화 ( 리팩토링후결과 ) public class Automobile { private String make; public String model; public void setmake(string make) { this.make = make; public String getmake() { return make; public class AutomobileTest { public void race() { Automobile car1 = new Automobile(); car1.setmake("austin Healy"); car1.model= "Sprite"; //...
Change Method Signature 마지막리팩토링 (Change Method Signature) 은메소드의매개변수, 가시성, 메소드의리턴타입을변경한다. 리팩토링되는메소드에서변경으로인해문제가생긴다면리팩토링작동은이를거부한다. 예제 public class MethodSigExample { public int test(string s, int i) { int x = i + s.length(); return x; class MethodSig2 { public void calltest() { MethodSigExample eg = new MethodSigExample(); int r = eg.test("hello", 10);
Change Method Signature MethodSigExample 클래스에서 test 를하이라이팅하고 Refactor > Change Method Signature 를선택한다. 다이얼로그박스가나타난다
Change Method Signature 이첫번째옵션은메소드의가시성을변경하는것이다. 이예제에서이를 protected 또는 private 으로변경한다. Eclipse 는리팩토링을실행하는동안문제가발견된곳을강조한다. 적절한값을선택하는것은여러분의몫이다
Change Method Signature 다음옵션은리턴유형을변경하는것이다. 예를들어리턴유형을 float 로변경한다. 이것은두번째클래스의 calltest() 에서문제를일으킨다. float 이 int 로변환될수없기때문이다. test() 에의해리턴된리턴값을 int 에캐스팅하거나 calltest() 의 r 타입을 float 으로변경한다 다음은파마미터를변경한다. String 에서 int 으로첫번째매개변수유형을변경한다면비슷한것이고려될수있다. 이것은리팩토링실행중정지된다. 리팩토링되고있는메소드에문제를일으키기때문이다 : int 은 length() 메소드를갖고있지않다.
Q&A