[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

 

//테스트 데이터
var users = [
  { id: 10, name: 'ID', age: 36 },
  { id: 20, name: 'BJ', age: 32 },
  { id: 30, name: 'JM', age: 32 },
  { id: 40, name: 'PJ', age: 27 },
  { id: 50, name: 'HA', age: 25 },
  { id: 60, name: 'JE', age: 26 },
  { id: 70, name: 'JI', age: 31 },
  { id: 80, name: 'MP', age: 23 }
];
//앞으로 함수를 담을 obj
const _ = (()=>{
    return {
        
    };
})();

 

filter

요소를 걸러내는 함수

인자로 받은 요소 수와 같거나 적은 요소를 리턴한다.

//함수를 담은 obj
const _ = (()=>{
    return {
        filter:filter ,
    };

/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
    //함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
    const result = [];
    for(const val of list){
        //보조함수를 인자로 받아 
        if(predi(val)) result.push(val);
    }
    return result;
}
})();
//나이 25세 이상 사용자 걸러내기
//절차지향 코드
const tmp = [];
for(const user of users){
    if(user.age>=25) tmp.push(user);
}
console.log(tmp);
//함수형 코드
console.log(
    _.filter(users, u=>u.age>=25)
);

console.log(
    _.filter(users, u=>u.age-(u.age%10) === 20), //20대만
    _.filter(users, u=>u.name.startsWith("J")) //J로 시작하는 user
);

map

요소를 변환시키는 함수

들어온 요소와 같은 수의 변환된 요소를 리턴한다.

/* 요소를 변환시키는 함수*/
function map(list , mapper){
    const result = [];
    for(const val of list)
        result.push(mapper(val));
    return result;
}
//map 사용
console.log(
    _.map(users, u=>u.age) ,
    _.map(users, u=>u.name) ,
    _.map(users, u=>u.id) ,
)

중간 점검

//함수를 담은 obj
const _ = (()=>{
    return {
        filter:filter ,
        map:map ,
    };

/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
    //함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
    const result = [];
    for(const val of list){
        //보조함수를 인자로 받아 
        if(predi(val)) result.push(val);
    }
    return result;
}
/* 요소를 변환시키는 함수*/
function map(list , mapper){
    const result = [];
    for(const val of list)
        result.push(mapper(val));
    return result;
}

})();

향상된 for문을 사용하고 있어 편리하지만, 분명히 반복문에서 코드 중복이 발생하고있다.

 

each

들어온 요소 리스트에서 값을 하나씩 꺼내어 주는 함수

function each(list, iter){
    for(const val of list) iter(val);
    return list;
}
_.each(users,u=>console.log(u));

filter, map 에 적용 후 동작 확인

//함수를 담은 obj
const _ = (()=>{
    return {
        filter:filter ,
        map:map ,
        each: each,
    };

/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
    //함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
    const result = [];
    //보조함수를 인자로 받아 
    each(list, val =>{
        if(predi(val)) result.push(val);
    });
    return result;
}
/* 요소를 변환시키는 함수*/
function map(list , mapper){
    const result = [];
    each(list, val => result.push(mapper(val)));
    return result;
}

function each(list, iter){
    for(const val of list) iter(val);
    return list;
}
})();

함수가 함수를 받아서 처리하는 것을 고차함수 혹은 응용형 함수라 한다.

함수 내부를 보면, 기존 값에 대한 변형 없이 항상 새로운 값을 리턴하고 있다. 그래서 코딩을 계속 진행하는 과정에도 원본 데이터에는 손상이 가지 않을 것을 알 수 있다.

섞어써보기

//섞어쓰기, 30세 이상, user 이름
console.log(
    _.map(_.filter(users, u=> u.age >=30), u=>u.name)
);

function.html
0.00MB

정의

프로토타입 인스턴스를 사용하여 생성할 객체의 종류를 지정하고 이 프로토타입을 복사하여 새로운 객체를 생성

 

코드

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class PrototypeEx {

	static abstract class Shape implements Cloneable {
		public int x;
		public int y;
		public String color;

		public Shape() {}

		protected Shape(Shape target) {
			if (target != null) {
				this.x = target.x;
				this.y = target.y;
				this.color = target.color;
			}
		}

		public abstract Shape clone();

		@Override
		public boolean equals(Object object2) {
			if (!(object2 instanceof Shape))
				return false;
			Shape shape2 = (Shape) object2;
			return shape2.x == x && shape2.y == y && Objects.equals(shape2.color, color);
		}
	}

	static class Circle extends Shape {
		public int radius;

		public Circle() {
		}

		public Circle(Circle target) {
			super(target);
			if (target != null) {
				this.radius = target.radius;
			}
		}

		// 공변 반환타입
		@Override
		public Circle clone() {
			return new Circle(this);
		}

		@Override
		public boolean equals(Object object2) {
			if (!(object2 instanceof Circle) || !super.equals(object2))
				return false;
			Circle shape2 = (Circle) object2;
			return shape2.radius == radius;
		}
	}

	static class Rectangle extends Shape {
		public int width;
		public int height;

		public Rectangle() {
		}

		public Rectangle(Rectangle target) {
			super(target);
			if (target != null) {
				this.width = target.width;
				this.height = target.height;
			}
		}

		// 공변 반환타입
		@Override
		public Rectangle clone() {
			return new Rectangle(this);
		}

		@Override
		public boolean equals(Object object2) {
			if (!(object2 instanceof Rectangle) || !super.equals(object2))
				return false;
			Rectangle shape2 = (Rectangle) object2;
			return shape2.width == width && shape2.height == height;
		}
	}

	public static void main(String[] args) {
		List<Shape> shapes = new ArrayList<>();
		List<Shape> shapesCopy = new ArrayList<>();

		Circle circle = new Circle();
		circle.x = 10;
		circle.y = 20;
		circle.radius = 15;
		circle.color = "red";

		Circle circle2 = circle.clone();
		circle2.x = 11;

		// 1
		shapes.add(circle);
		shapesCopy.add(circle);
		// 2
		shapes.add(circle);
		shapesCopy.add(circle2);

		Rectangle rectangle = new Rectangle();
		rectangle.width = 10;
		rectangle.height = 20;
		rectangle.color = "blue";

		Rectangle rectangle2 = rectangle.clone();
		// 3
		shapes.add(rectangle);
		shapesCopy.add(rectangle2);

		cloneAndCompare(shapes, shapesCopy);
	}

	private static void cloneAndCompare(List<Shape> shapes, List<Shape> shapesCopy) {

		for (int i = 0; i < shapes.size(); i++) {
			if (shapes.get(i) != shapesCopy.get(i)) {
//            	System.out.println(System.identityHashCode(shapes.get(i))+" "+System.identityHashCode(shapesCopy.get(i)));
				System.out.print(i + ": 도형이 서로 다른 객체입니다.");
				if (shapes.get(i).equals(shapesCopy.get(i))) {
					System.out.println(i + ": 그리고 같은 값을 가집니다.");
				} else {
					System.out.println(i + ": 그리고 다른 값입니다.");
				}
			} else {
				System.out.println(i + ": 도형이 서로 같은 객체입니다.");
			}
		}
	}
}

결과

0: 도형이 서로 같은 객체입니다.
1: 도형이 서로 다른 객체입니다.1: 그리고 다른 값입니다.
2: 도형이 서로 다른 객체입니다.2: 그리고 같은 값을 가집니다.

 

예시로만 알아두고, 실제 "Cloneable"는 제한적으로 사용해야 한다.

예시는 슈퍼클래스가 abstract라 사용했다. 

이펙티브 자바에 아이템 13 부분에선 Cloneable 대신 복사 생성자나 복사 팩터리를 사용하라 권고한다.(배열은 제외)

위 예시에서 복사 생성자를 사용했다.

 

정의

flyweight는 다른 유사한 개체와 가능한 한 많은 데이터를 공유하여 메모리 사용을 최소화하는 패턴

하나의 인스턴스만으로 상태값만 변경해가며, 사용

주 목적은 메인메모리(RAM) 절약

 

코드

import java.time.LocalDate;
import java.time.Month;

public class FlyweightEx {
	//공통 인터페이스
	static interface Tree {
		public void display(int x, int y);
		public default boolean isWithinRange(LocalDate aDate) {
			Month month = aDate.getMonth();
			return (month.getValue() > 2) && (month.getValue() < 11);
		}
	}
	static class ConiferTree implements Tree {
		public void display(int x, int y) {
			System.out.println("침엽수 위치 : " + x + ", " + y);
		}
	}
	static class DeciduousTree implements Tree {
		public void display(int x, int y) {
			System.out.println("낙엽수 위치 : " + x + ", " + y);
			if (!this.isWithinRange(LocalDate.now())) {
				System.out.println("현재 계절엔 낙엽이 없습니다.");
			}
		}
	}
	//팩토리로 생성을 관리
	static class TreeFactory {
		Tree d, c = null;
		public TreeFactory() {
			this.d = new DeciduousTree();
			this.c = new ConiferTree();
		}
		public Tree getTree(String type) throws Exception {
			if (type.equals("침엽수")) {
				return this.d;
			} else if (type.equals("낙엽수")) {
				return this.c;
			} else {
				throw new Exception("지원하지 않는 종류");
			}
		}
	}
	
	public static void main(String[] args) {
		//플레이웨이트 클래스에서 상태만을 때어 별도로 관리
		//상태가 바뀔때마다 마치 새로운 인스턴스 처럼 보이지만, 하나의 인스턴스
		int[][] deciduousLocations = {{1, 1}, {33, 50}, {100, 90}};
		int[][] coniferLocations = {{10, 87}, {24, 76}, {2, 64}};
		
		TreeFactory treeFactory = new TreeFactory();
		Tree d, c;
		try {
			d = treeFactory.getTree("낙엽수");
			c = treeFactory.getTree("침엽수");
			for (int[] location : deciduousLocations) {
				d.display(location[0],  location[1]);
			}
			for (int[] location : coniferLocations) {
				c.display(location[0],  location[1]);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

결과

침엽수 위치 : 1, 1
침엽수 위치 : 33, 50
침엽수 위치 : 100, 90
낙엽수 위치 : 10, 87
낙엽수 위치 : 24, 76
낙엽수 위치 : 2, 64

웹 프로그래밍에선 쓰이는 곳을 찾기가 더 힘들 듯하다.

정의

책임 연쇄 패턴은 핸들러들의 체인을 따라 요청을 처리하는 행동 디자인 패턴입니다.

따라서 순서가 굉장히 중요합니다. 

각 핸들러는 주어진 요청에 대한 처리를 담당하게 됩니다. (요청을  처리할지 말지, 다음 체인으로 넘길지 등등)

 

 

코드

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class CoREx {
	//커맨드 객체
	static class Person {
		String name;
		String sex;
		int age;
		public Person(String name, String sex, int age) {
			this.name = name;
			this.sex = sex;
			this.age = age;
		}
		@Override
		public String toString() {
			return "Person [name=" + name + ", sex=" + sex + ", age=" + age + "]";
		}
	}
	//각 요소를 요청이라고 가정한다.
	static List<Person> persons = new ArrayList<>();
	static {
		persons.add(new Person("김필터", "남", 20));
		persons.add(new Person("임자바", "남", 33));
		persons.add(new Person("이자바", "여", 22));
		persons.add(new Person("김자바", "여", 18));
		persons.add(new Person("이육사", "남", 27));
		persons.add(new Person("김육사", "여", 15));
		persons.add(new Person("박세종", "남", 30));
		persons.add(new Person("김세종", "남", 30));
	}
	//핸들러 
	static class Filter<T>{
		//나의 다음 체인 참조변수
		private Filter<T> nextFilter;
		//요구 사항
		private final Predicate<T> predi;
		
		protected Filter(Predicate<T> predi) {
			this.predi = predi;
		}
		@SafeVarargs //필터 체인 형성
		public static <T> Filter<T> createFilterChain(Filter<T> first, Filter<T>...filters){
			Filter<T> nowFilter = first;
			for(Filter<T> filter : filters) {
				nowFilter.nextFilter = filter;
				nowFilter = filter;
			}
			return first;
		}
		public boolean check(T data) {
			return predi.test(data) ? nextCheck(data) : false;
		}
		public boolean nextCheck(T data) {
			return nextFilter == null ? true : nextFilter.check(data);
		}
	}
	
	public static void main(String[] args) {
		//필터체인
		Filter<Person> filterChain = Filter.createFilterChain(
				new Filter<>(per -> per.age>=20)
				,new Filter<>(per -> per.sex.startsWith("남"))
				,new Filter<>(per -> per.name.startsWith("김"))
				);
		for(Person person : persons) {
			if(filterChain.check(person)) {
				System.out.println(person);
			}
		}
	}
}

결과

Person [name=김필터, sex=남, age=20]
Person [name=김세종, sex=남, age=30]

Filter는 사실 인터페이스나 추상클래스로 다루는 것이 더 좋으나, 편의상 일반 클래스로 구현

 

유사한 구조는 스프링 시큐리티에서 볼 수 있다.

 

 

'개발 > 디자인 패턴' 카테고리의 다른 글

생성 - 프로토타입(Prototype)  (0) 2023.05.13
구조 - 플라이웨이트(Flyweight)  (0) 2023.05.09
구조 - 빌더 패턴(Builder)  (0) 2023.05.03

정의

복잡한 객체들을 단계별로 생성할 수 있도록 하는 생성 디자인 패턴입니다.

이 패턴을 사용하면 같은 제작 코드를 사용하여 객체의 다양한 유형들과 표현을 제작할 수 있습니다.

 

빌더 패턴은 텔레스코핑 생성자 안티 패턴에 대한 해결책을 제공합니다.

public class BuilderEx {
	static class House{
		String interior; //내장재
		String exterior; //외장재
		int floor; // 층수
		int room;  // 방수
		int window;// 창문수
		
		public House(String interior, String exterior, int floor, int room, int window) {
			super();
			this.interior = interior;
			this.exterior = exterior;
			this.floor = floor;
			this.room = room;
			this.window = window;
		}

		@Override
		public String toString() {
			return "House [interior=" + interior + ", exterior=" + exterior + ", floor=" + floor + ", room=" + room
					+ ", window=" + window + "]";
		}
	}
	
	public static void main(String[] args) {
		System.out.println(new House("황토", "나무", 2, 4, 10));
        //이 코드 한줄만 보고 뭐가 내장재인지, 외장재인지
        //뭐가 층수, 방수, 창문수인지 알 수가 없다.
	}

위 처럼 생성자 인수가 늘어날 수록 순서를 신경써야한다.

 

빌더 적용

	//빌더
	static interface Builder{
		//편의상 메서드 체이닝을 적용한 경우가 많다.
		Builder setInterior(String interior);
		Builder setExterior(String exterior);
		Builder setFloor(int floor);
		Builder setRoom(int room);
		Builder setWindow(int window);
		//생성 메서드는 단일 계층 구조엔 상관없으나, 다중 계층구조이면 서브클래스에 위임한다.
		//리턴하는 객체 타입이 다르기 때문이다.
		House build();
	}
	
	static class HouseBuilder implements Builder{
		//기존 코드가 설정자 메서드를 지원한하기에 임시로 값을 저장해둔다.
		String interior; //내장재
		String exterior; //외장재
		int floor; // 층수
		int room;  // 방수
		int window;// 창문수
		
		public Builder setInterior(String interior) {
			this.interior = interior;
			return this;
		}
		public Builder setExterior(String exterior) {
			this.exterior = exterior;
			return this;
		}
		public Builder setFloor(int floor) {
			this.floor = floor;
			return this;
		}
		public Builder setRoom(int room) {
			this.room = room;
			return this;
		}
		public Builder setWindow(int window) {
			this.window = window;
			return this;
		}
		@Override
		public House build() {
			//검증 로직이 존재할 수도 있을 것, 혹은 기본값으로 설정되지 않은 값을 대신 할 수도 있다.
			if(interior == null && exterior == null 
					&& floor == 0&& room == 0&& window == 0) {
				//기본적으로 빌더 패턴은 사용자가 생성구조를 잘 파악하고 있어야 함을 전제로 한다.
				throw new IllegalArgumentException("인수가 부족합니다.");
			}
			try {
				return new House(interior, exterior, floor, room, window);
			}finally {
				interior = null;
				exterior = null;
				floor = 0;
				room = 0;
				window = 0;
			}
		}
	}
	
	public static void main(String[] args) {
		Builder houseBuilder = new HouseBuilder();
		houseBuilder.setExterior("나무");
		houseBuilder.setInterior("대리석");
		houseBuilder.setFloor(2);
		houseBuilder.setRoom(5);
		houseBuilder.setWindow(10);
		System.out.println(houseBuilder.build());
		houseBuilder.setExterior("콘크리트")
					.setInterior("유리")
					.setFloor(1)
					.setRoom(1)
					.setWindow(10);
		System.out.println(houseBuilder.build());
	}

빌더 패턴에선 기본적으로 사용자가 생성할 객체 구성을 직접 디자인한다. 따라서 잘알고 있다고 가정을 한다.

이렇게 객체 구조를 잘 알아야한다는 것이 단점이다.

디렉터

디렉터 클래스를 두어 자주쓰는 구성 정보를 재사용할 수도 있다.

	static class HouseDirector{
		public void house1(Builder builder) {
			builder.setExterior("나무");
			builder.setInterior("대리석");
			builder.setFloor(2);
			builder.setRoom(5);
			builder.setWindow(10);
		}
		public void house2(Builder builder) {
			builder.setExterior("콘크리트")
				.setInterior("유리")
				.setFloor(1)
				.setRoom(1)
				.setWindow(10);
		}
	}
	
	public static void main(String[] args) {
		Builder houseBuilder = new HouseBuilder();
		HouseDirector houseDirector = new HouseDirector();
		houseDirector.house1(houseBuilder);
		System.out.println(houseBuilder.build());
		houseDirector.house2(houseBuilder);
		System.out.println(houseBuilder.build());
	}

builder.zip
0.00MB

자주 보는 클래스에 빌더 패턴 사례

 

컴포지트 패턴을 보니 문득 파일 시스템과 유사하게 느껴져

폴더를 하나 지정해, 그 폴더를 포함한 하위 요소를 간이 컴포지트 패턴 틀에 옮겨봤다.

package designpattern.structural.composite;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class 폴더파일 {
	static int cnt = 0;
	
	//component 
	abstract static class FolderOrFile{
		private String name;
		private boolean isFolder;
		
		protected FolderOrFile(String name, boolean isFolder) {
			this.name = name;
			this.isFolder = isFolder;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public boolean isFolder() {
			return isFolder;
		}
		public void setFolder(boolean isFolder) {
			this.isFolder = isFolder;
		}
		public void add(FolderOrFile folderOrFile) {
			throw new UnsupportedOperationException();
		}
		public void remove(FolderOrFile folderOrFile) {
			throw new UnsupportedOperationException();
		}
		public void print() {
			throw new UnsupportedOperationException();
		}
		
	}
	//composite
	static class MyFolder extends FolderOrFile{
		List<FolderOrFile> folderOrFiles = new ArrayList<>();
		
		@Override
		public void add(FolderOrFile folderOrFile) {
			this.folderOrFiles.add(folderOrFile);
		}
		
		public MyFolder(String name, boolean isFolder) {
			super(name, isFolder);
		}
		@Override
		public void print() {
			for(FolderOrFile fof : folderOrFiles) {
				System.out.println(cnt+++" "+fof);
			}
		}
		@Override
		public String toString() {
			return "[Folder]"+getName();
		}
		
	}
	//leaf
	static class MyFile extends FolderOrFile{
		private String description;
		
		public MyFile(String name, boolean isFolder, String description) {
			super(name, isFolder);
			this.description = description;
		}
		public String getDescription() {
			return description;
		}
		public void setDescription(String description) {
			this.description = description;
		}
		@Override
		public void print() {
			System.out.println(cnt+++" "+this);
		}
		@Override
		public String toString() {
			return "[File]"+getName()+" "+getDescription();
		}
	}
	
	public static void main(String[] args) {
		File file = new File("c:/git");
		
		FolderOrFile fof = new MyFolder(file.getName(), true);
		
		copy(file, fof);
		
		fof.print();
		
	}
	static void copy(File file, FolderOrFile folderOrFile) {
		if(file.isDirectory()) {
			folderOrFile.add(new MyFolder(file.getName(), true));
			for(File f : file.listFiles()) {
				copy(f , folderOrFile);
			}
		}else {
			folderOrFile.add(new MyFile(file.getName(), false, file.length()+"바이트" ));
		}
	}
}

 

824 [File]9de29bb2d1d6434b8b29ae775ad8c2e48c5391 15바이트
825 [Folder]ef
826 [File]7795d074c4633075a0a241bc9f86a4b402d584 145바이트
827 [Folder]info
828 [Folder]pack
829 [File]ORIG_HEAD 41바이트
830 [Folder]refs
831 [Folder]heads
832 [File]master 41바이트
833 [Folder]tags
834 [File]asd.txt 0바이트
835 [File]README.md 8바이트
836 [File]reset.txt 7바이트

 

'개발 > 자바(JAVA)' 카테고리의 다른 글

ParameterizedTypeReference(SuperTypeToken)  (0) 2023.06.30
SuperTypeToken  (0) 2023.06.29
컴포지트 패턴 연습 - 1  (0) 2023.04.13
기본형 배열 List로 변환하기  (0) 2023.03.10
객체 지향 - 6  (0) 2023.02.03

컴포지트 패턴을 공부하다. 갑자기 파일 시스템이랑 비슷한 것 같아서 연습해봄.

 

컴포지트 패턴이란

개체를 트리 구조로 구성하여 부분-전체 계층을 나타냅니다.


이 패턴을 사용하면 클라이언트가 개별 개체와 개체 구성(컴포지트)을 동일하게 처리할 수 있습니다.

 

구성요소 

component

컴포지션의 모든 개체에 대한 기본 인터페이스입니다. 하위 조합을 관리하는 공통 메서드가 있는 인터페이스 또는 추상 클래스여야 합니다.


leaf

자식이 없는 노드를 의미합니다.


composite

자식이 있는 노드를 의미합니다. 이때 다른 composite도 포함될 수 있습니다.

 

코드 예시

import java.util.ArrayList;
import java.util.List;

public class SimpleComposite {
	
	abstract static class Component{
		String name;
		
		void addComponent(Component component) {
			throw new UnsupportedOperationException();
		}
		abstract void print();
	}
	
	static class Leaf extends Component{
		String name;
		
		public Leaf(String name) {
			this.name = name;
		}



		void print() {
			System.out.println("[Leaf]"+name);
		}
	}
	static class Composite extends Component{
		String name;
		List<Component> components = new ArrayList<>();
		
		public Composite(String name) {
			this.name = name;
		}
		@Override
		void addComponent(Component component) {
			components.add(component);
		}
		void print() {
			System.out.println("[Composite]"+name);
			for(Component component : components) {
				component.print();
			}
		}
	}
	
	public static void main(String[] args) {
		Component root = new Composite("회장님");
		root.addComponent(new Leaf("홍 비서"));
		root.addComponent(new Leaf("박 비서"));
		
		Component composite1 = new Composite("전무");
		composite1.addComponent(new Leaf("김 비서"));
		Component composite2 = new Composite("이사");
		composite2.addComponent(new Leaf("이 비서"));
		
		root.addComponent(composite1);
		root.addComponent(composite2);
		
		Component composite1_1 = new Composite("영업팀");
		composite1_1.addComponent(new Leaf("임꺽정 팀장"));
		
		Component composite2_1 = new Composite("기획팀");
		composite2_1.addComponent(new Leaf("홍길동 팀장"));
		
		composite1.addComponent(composite1_1);
		composite2.addComponent(composite2_1);
		
		
		root.print();
	}
	
}
[Composite]회장님
[Leaf]홍 비서
[Leaf]박 비서
[Composite]전무
[Leaf]김 비서
[Composite]영업팀
[Leaf]임꺽정 팀장
[Composite]이사
[Leaf]이 비서
[Composite]기획팀
[Leaf]홍길동 팀장

 

 

간단한 조직도를 만들어봤다. 

 

만든 김에 트리처럼 레벨로 출력해보기

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class SimpleComposite {
	
	abstract static class Component{
		
		abstract String getName();
		abstract boolean isLeaf();
		void addComponent(Component component) {
			throw new UnsupportedOperationException();
		}
		List<Component> getComponents(){
			throw new UnsupportedOperationException();
		}
		
		abstract void print();
	}
	
	static class Leaf extends Component{
		String name;
		public String getName() {
			return name;
		}
		boolean isLeaf() {
			return true;
		}
		public Leaf(String name) {
			this.name = name;
		}


		void print() {
			System.out.println("[Leaf]"+name);
		}
	}
	static class Composite extends Component{
		String name;
		List<Component> components = new ArrayList<>();
		
		public Composite(String name) {
			this.name = name;
		}
		public String getName() {
			return name;
		}
		boolean isLeaf() {
			return false;
		}
		void addComponent(Component component) {
			components.add(component);
		}
		public List<Component> getComponents() {
			return components;
		}
		void print() {
			System.out.println("[Composite]"+name);
			for(Component component : components) {
				component.print();
			}
		}
	}
	
	public static void main(String[] args) {
		Component root = new Composite("회장님");
		root.addComponent(new Leaf("홍 비서"));
		root.addComponent(new Leaf("박 비서"));
		
		Component composite1 = new Composite("전무");
		composite1.addComponent(new Leaf("김 비서"));
		Component composite2 = new Composite("이사");
		composite2.addComponent(new Leaf("이 비서"));
		
		root.addComponent(composite1);
		root.addComponent(composite2);
		
		Component composite1_1 = new Composite("영업팀");
		composite1_1.addComponent(new Leaf("임꺽정 팀장"));
		
		Component composite2_1 = new Composite("기획팀");
		composite2_1.addComponent(new Leaf("홍길동 팀장"));
		
		composite1.addComponent(composite1_1);
		composite2.addComponent(composite2_1);
		
		
//		root.print();
		BFS(root);
	}
	
	static void BFS(Component root) {
		Queue<Component> components = new LinkedList<>();
		components.offer(root);
		int lv = 1;
		
		while(!components.isEmpty()) {
			int len = components.size();
			System.out.print(lv++ +" ");
			for(int i=0;i<len;i++) {
				Component compo = components.poll();
				System.out.print(compo.getName()+" ");
				if(!compo.isLeaf()) {
					for(Component com : compo.getComponents()) {
						components.add(com);
					}
				}
			}
			System.out.println();
		}
	}
	
}
1 회장님 
2 홍 비서 박 비서 전무 이사 
3 김 비서 영업팀 이 비서 기획팀 
4 임꺽정 팀장 홍길동 팀장

 

 

 

 

 

 

 

 

 

'개발 > 자바(JAVA)' 카테고리의 다른 글

SuperTypeToken  (0) 2023.06.29
컴포지트 패턴 연습 - 2  (0) 2023.04.16
기본형 배열 List로 변환하기  (0) 2023.03.10
객체 지향 - 6  (0) 2023.02.03
객체 지향 - 5  (0) 2023.02.01
function round(number, position){
    if(typeof number !== 'number') return number;
    if(Number.isNaN(number) || !Number.isFinite(number)) return number;
    if(typeof position !== 'number') return number;
    if(Number.isNaN(position) || !Number.isFinite(position)) return number;
    if(Math.abs(position)>15) return number;
	// 능력것 유효성 추가...
    
    var dp = 1;

    if(position == 0) return Math.round(number);
    
    for (let i = 0; i < Math.abs(position); i++)  dp *=10;

    if(position<0) return Math.round(number/dp)*dp; 
    else return Math.round(number*dp)/dp; 
}

 

<!DOCTYPE html>
<html lang="en">
<script src="round.js" ></script>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>function test</title>
</head>
<body>
    
</body>
<script>
    const number = 123456789.123456789;
    console.log(number);
    console.log('round(number)', round(number));
    console.log('round(number)', round(number,0));
    console.log('round(number,-1)', round(number,-1));
    console.log('round(number,-5)', round(number,-5));
    console.log('round(number, 1)', round(number,1));
    console.log('round(number, 5)', round(number,5));
    console.log(round('string'));
    console.log(round(number , 'string'));
    console.log(round(null),round(Infinity),round(NaN));

</script>

</html>

테스트

 

 

 

 

 

 

'개발 > 자바스크립트' 카테고리의 다른 글

구조 분해 할당  (0) 2023.08.21
배열은 객체?  (0) 2023.08.18
평균 구하기 주의점  (0) 2023.01.21
자바스크립트 중복제거 - 2  (0) 2022.12.15
자바스크립트 중복 제거  (0) 2022.12.12
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Test {
    public static void main(String[] args) {
        
        List<int[]> list = IntStream.rangeClosed(1, 10)
            .mapToObj(n->getIntArray())
            .collect(Collectors.toList());
        
        
        //Arrays.stream은 인자로 int[]을 받아 우리가 생각하는 int하나하나를 요소로하는 스트림을 만든다.
        IntStream intStream = Arrays.stream(getIntArray());
        //Arrays.asList는 얼핏보면 List<Integer>로 만들어 줄 것 같지만, 아니다.
        List<int[]> list2 = Arrays.asList(getIntArray());
        
        //방법 1 반복문 이용
        List<Integer> list3 = arrayToList(getIntArray());
        //방법 2 스트림 이용
        List<Integer> list4 = intStream.boxed().collect(Collectors.toList());
        
        //번외, List<int[]> List<Integer>로 변환
        List<Integer> list5 = list.stream()
            .map(Test::arrayToList)
            .flatMap(li->li.stream())
            .collect(Collectors.toList());
    }
    
    static List<Integer> arrayToList(int[] arr){
        List<Integer> tmpList = new ArrayList<>();
        for(int v : arr) {
            tmpList.add(v);
        }
        return tmpList;
    }
    
    static int[] getIntArray() {
        return ThreadLocalRandom.current()
                .ints(1, 101)
                .limit(50)
                .toArray();
    }
    
}

 

구아바 같은 라이브러리를 사용하지 않고는

기본형 배열에서 리스트로 변환은 방법1이 가장 현실적인 것 같다.

 

 

 

'개발 > 자바(JAVA)' 카테고리의 다른 글

컴포지트 패턴 연습 - 2  (0) 2023.04.16
컴포지트 패턴 연습 - 1  (0) 2023.04.13
객체 지향 - 6  (0) 2023.02.03
객체 지향 - 5  (0) 2023.02.01
객체 지향 - 4  (0) 2023.01.31

 다중 컨테이너를 연결하여 서로 상호 작용할 수 있는 방법

 

도커화된 컨테이너가 통신하는 케이스 몇가지

  • HTTP를 이용한 통신
  • 컨테이너에서 로컬 호스트 머신으로 통신
  • 컨테이너가 또 다른 컨테이너로 통신

 

도커 HTTP 통신

도커화된 애플리케이션은 별도 설정 없이 API 페이지와 통신 있다.

 

 

도커 컨테이너에서 호스트 머신으로 통신법

host.docker.internal 특수 도메인 사용

도커가 이해할 있는 특수 도메인으로 도커 컨테이너 내부의 호스트 머신 IP주소로 변환된다.

 

 

컨테이너에서 다른 컨테이너 통신

 

명령어 결과에 IP정보가 있다.

 

 

매번 이렇게 생성된 IP를 확인하며 사용하는 것은 불편하다.

 

컨테이너 쉬운 통신방법

docker run --network 옵션 사용

 볼륨과 다르게 도커가 자동으로 생성해주지 않기에 직접 network 만들어줘야 한다.

 

 

같은 네트워크로 묶는다.

 

IP하드 코딩 없이 성공

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts