diff --git a/.idea/2021-Java-Study.iml b/.idea/2021-Java-Study.iml new file mode 100644 index 00000000..d6ebd480 --- /dev/null +++ b/.idea/2021-Java-Study.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..40674af8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..7fabbc98 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..c40d1f3d --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + 1617259629172 + + + + + + \ No newline at end of file diff --git a/CQRS.md b/CQRS.md new file mode 100644 index 00000000..7985879f --- /dev/null +++ b/CQRS.md @@ -0,0 +1,25 @@ +# 자바 기초 +## CQRS +> CQRS는 Command and Query Responsibility Segregation(명령과 조회의 책임 분리)을 나타낸다. 이름처럼 시스템에서 명령을 처리하는 책임과 조회를 처리하는 책임을 분리하는 것이 CQRS의 핵심이다. + + + +- 명령(Command) : 시스템의 상태를 변경하는 작업 + - ex) 주문 취소, 배송 완료 +- 쿼리(Query) : 시스템의 상태를 반환하는 작업 + - ex) 주문 목록 +- 책임(Responsibility) : 구성 요소의 역할 + - 구성 요소 : 클래스, 함수, 모듈/패키지, 웹서버/DB + +- 분리(Segregation) : 역할에 따라 구성 요소 나누기 + +### 명령과 조회에 단일 모델을 사용한다면? +🔍 명령과 쿼리가 다루는 데이터가 다르기 때문에 이도 저도 아닌 잡탕이 된다. +- 코드 역할/책임 모호 +- 의미/가독성 등 나빠짐 +- 유지보수성이 떨어짐 + - 명령과 쿼리는 코드 변경 빈도/사용자가 다르기 때문 + - 기능마다 요구하는 성능이 다르기 때문 + + + diff --git a/racingcar/.gradle/6.7/executionHistory/executionHistory.bin b/racingcar/.gradle/6.7/executionHistory/executionHistory.bin new file mode 100644 index 00000000..fa5d6d6b Binary files /dev/null and b/racingcar/.gradle/6.7/executionHistory/executionHistory.bin differ diff --git a/racingcar/.gradle/6.7/executionHistory/executionHistory.lock b/racingcar/.gradle/6.7/executionHistory/executionHistory.lock new file mode 100644 index 00000000..8dba8f0d Binary files /dev/null and b/racingcar/.gradle/6.7/executionHistory/executionHistory.lock differ diff --git a/racingcar/.gradle/6.7/fileChanges/last-build.bin b/racingcar/.gradle/6.7/fileChanges/last-build.bin new file mode 100644 index 00000000..f76dd238 Binary files /dev/null and b/racingcar/.gradle/6.7/fileChanges/last-build.bin differ diff --git a/racingcar/.gradle/6.7/fileHashes/fileHashes.bin b/racingcar/.gradle/6.7/fileHashes/fileHashes.bin new file mode 100644 index 00000000..661393ed Binary files /dev/null and b/racingcar/.gradle/6.7/fileHashes/fileHashes.bin differ diff --git a/racingcar/.gradle/6.7/fileHashes/fileHashes.lock b/racingcar/.gradle/6.7/fileHashes/fileHashes.lock new file mode 100644 index 00000000..a02bace5 Binary files /dev/null and b/racingcar/.gradle/6.7/fileHashes/fileHashes.lock differ diff --git a/racingcar/.gradle/6.7/gc.properties b/racingcar/.gradle/6.7/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/racingcar/.gradle/6.7/javaCompile/javaCompile.lock b/racingcar/.gradle/6.7/javaCompile/javaCompile.lock new file mode 100644 index 00000000..d5d6533a Binary files /dev/null and b/racingcar/.gradle/6.7/javaCompile/javaCompile.lock differ diff --git a/racingcar/.gradle/6.7/javaCompile/taskHistory.bin b/racingcar/.gradle/6.7/javaCompile/taskHistory.bin new file mode 100644 index 00000000..3f5afb7e Binary files /dev/null and b/racingcar/.gradle/6.7/javaCompile/taskHistory.bin differ diff --git a/racingcar/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/racingcar/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 00000000..080f075f Binary files /dev/null and b/racingcar/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/racingcar/.gradle/buildOutputCleanup/cache.properties b/racingcar/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 00000000..f53ce95e --- /dev/null +++ b/racingcar/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Mon Apr 12 17:58:13 KST 2021 +gradle.version=6.7 diff --git a/racingcar/.gradle/buildOutputCleanup/outputFiles.bin b/racingcar/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 00000000..36368bec Binary files /dev/null and b/racingcar/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/racingcar/.gradle/checksums/checksums.lock b/racingcar/.gradle/checksums/checksums.lock new file mode 100644 index 00000000..f0f1452d Binary files /dev/null and b/racingcar/.gradle/checksums/checksums.lock differ diff --git a/racingcar/.gradle/configuration-cache/gc.properties b/racingcar/.gradle/configuration-cache/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/racingcar/.gradle/vcs-1/gc.properties b/racingcar/.gradle/vcs-1/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/racingcar/.idea/.gitignore b/racingcar/.idea/.gitignore new file mode 100644 index 00000000..ed950358 --- /dev/null +++ b/racingcar/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/../../../../../../:\Users\nohyg\IdeaProjects\racingcar\.idea/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/racingcar/.idea/compiler.xml b/racingcar/.idea/compiler.xml new file mode 100644 index 00000000..61a9130c --- /dev/null +++ b/racingcar/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/racingcar/.idea/gradle.xml b/racingcar/.idea/gradle.xml new file mode 100644 index 00000000..02b3e5bf --- /dev/null +++ b/racingcar/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/racingcar/.idea/jarRepositories.xml b/racingcar/.idea/jarRepositories.xml new file mode 100644 index 00000000..fdc392fe --- /dev/null +++ b/racingcar/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/racingcar/.idea/misc.xml b/racingcar/.idea/misc.xml new file mode 100644 index 00000000..2266f6bc --- /dev/null +++ b/racingcar/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/racingcar/.idea/modules/racingcar.iml b/racingcar/.idea/modules/racingcar.iml new file mode 100644 index 00000000..5e3f563c --- /dev/null +++ b/racingcar/.idea/modules/racingcar.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/racingcar/build.gradle b/racingcar/build.gradle new file mode 100644 index 00000000..95687b5b --- /dev/null +++ b/racingcar/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group 'org.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/racingcar/build/classes/java/main/Application.class b/racingcar/build/classes/java/main/Application.class new file mode 100644 index 00000000..861c6036 Binary files /dev/null and b/racingcar/build/classes/java/main/Application.class differ diff --git a/racingcar/build/classes/java/main/racingcar/Car.class b/racingcar/build/classes/java/main/racingcar/Car.class new file mode 100644 index 00000000..8fe4c1d1 Binary files /dev/null and b/racingcar/build/classes/java/main/racingcar/Car.class differ diff --git a/racingcar/build/classes/java/main/racingcar/Cars.class b/racingcar/build/classes/java/main/racingcar/Cars.class new file mode 100644 index 00000000..97b65fbf Binary files /dev/null and b/racingcar/build/classes/java/main/racingcar/Cars.class differ diff --git a/racingcar/build/classes/java/main/racingcar/Game.class b/racingcar/build/classes/java/main/racingcar/Game.class new file mode 100644 index 00000000..1746d845 Binary files /dev/null and b/racingcar/build/classes/java/main/racingcar/Game.class differ diff --git a/racingcar/build/classes/java/main/racingcar/Winner.class b/racingcar/build/classes/java/main/racingcar/Winner.class new file mode 100644 index 00000000..d388d4b6 Binary files /dev/null and b/racingcar/build/classes/java/main/racingcar/Winner.class differ diff --git a/racingcar/build/classes/java/main/utils/RandomUtils.class b/racingcar/build/classes/java/main/utils/RandomUtils.class new file mode 100644 index 00000000..b37e7178 Binary files /dev/null and b/racingcar/build/classes/java/main/utils/RandomUtils.class differ diff --git a/racingcar/build/classes/java/main/view/InputView.class b/racingcar/build/classes/java/main/view/InputView.class new file mode 100644 index 00000000..24e1815c Binary files /dev/null and b/racingcar/build/classes/java/main/view/InputView.class differ diff --git a/racingcar/build/classes/java/main/view/ResultView.class b/racingcar/build/classes/java/main/view/ResultView.class new file mode 100644 index 00000000..8326b991 Binary files /dev/null and b/racingcar/build/classes/java/main/view/ResultView.class differ diff --git a/racingcar/build/tmp/compileJava/source-classes-mapping.txt b/racingcar/build/tmp/compileJava/source-classes-mapping.txt new file mode 100644 index 00000000..b58b8b59 --- /dev/null +++ b/racingcar/build/tmp/compileJava/source-classes-mapping.txt @@ -0,0 +1,16 @@ +racingcar/Winner.java + racingcar.Winner +racingcar/Game.java + racingcar.Game +view/ResultView.java + view.ResultView +Application.java + Application +view/InputView.java + view.InputView +racingcar/Car.java + racingcar.Car +racingcar/Cars.java + racingcar.Cars +utils/RandomUtils.java + utils.RandomUtils diff --git a/racingcar/gradle/wrapper/gradle-wrapper.jar b/racingcar/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/racingcar/gradle/wrapper/gradle-wrapper.jar differ diff --git a/racingcar/gradle/wrapper/gradle-wrapper.properties b/racingcar/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..be52383e --- /dev/null +++ b/racingcar/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/racingcar/gradlew b/racingcar/gradlew new file mode 100644 index 00000000..4f906e0c --- /dev/null +++ b/racingcar/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/racingcar/gradlew.bat b/racingcar/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/racingcar/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/racingcar/out/production/classes/Application.class b/racingcar/out/production/classes/Application.class new file mode 100644 index 00000000..861c6036 Binary files /dev/null and b/racingcar/out/production/classes/Application.class differ diff --git a/racingcar/out/production/classes/StreamTest.class b/racingcar/out/production/classes/StreamTest.class new file mode 100644 index 00000000..d23c190d Binary files /dev/null and b/racingcar/out/production/classes/StreamTest.class differ diff --git a/racingcar/out/production/classes/racingcar/Car.class b/racingcar/out/production/classes/racingcar/Car.class new file mode 100644 index 00000000..7bd5cebe Binary files /dev/null and b/racingcar/out/production/classes/racingcar/Car.class differ diff --git a/racingcar/out/production/classes/racingcar/Cars.class b/racingcar/out/production/classes/racingcar/Cars.class new file mode 100644 index 00000000..52c8d4d2 Binary files /dev/null and b/racingcar/out/production/classes/racingcar/Cars.class differ diff --git a/racingcar/out/production/classes/racingcar/Game.class b/racingcar/out/production/classes/racingcar/Game.class new file mode 100644 index 00000000..faf03591 Binary files /dev/null and b/racingcar/out/production/classes/racingcar/Game.class differ diff --git a/racingcar/out/production/classes/racingcar/Winner.class b/racingcar/out/production/classes/racingcar/Winner.class new file mode 100644 index 00000000..d388d4b6 Binary files /dev/null and b/racingcar/out/production/classes/racingcar/Winner.class differ diff --git a/racingcar/out/production/classes/utils/RandomUtils.class b/racingcar/out/production/classes/utils/RandomUtils.class new file mode 100644 index 00000000..e79d6ff3 Binary files /dev/null and b/racingcar/out/production/classes/utils/RandomUtils.class differ diff --git a/racingcar/out/production/classes/view/InputView.class b/racingcar/out/production/classes/view/InputView.class new file mode 100644 index 00000000..ed04c004 Binary files /dev/null and b/racingcar/out/production/classes/view/InputView.class differ diff --git a/racingcar/out/production/classes/view/ResultView.class b/racingcar/out/production/classes/view/ResultView.class new file mode 100644 index 00000000..901d55d1 Binary files /dev/null and b/racingcar/out/production/classes/view/ResultView.class differ diff --git a/racingcar/settings.gradle b/racingcar/settings.gradle new file mode 100644 index 00000000..563821bb --- /dev/null +++ b/racingcar/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'racingcar' + diff --git a/racingcar/src/main/java/Application.java b/racingcar/src/main/java/Application.java new file mode 100644 index 00000000..a16bc9a2 --- /dev/null +++ b/racingcar/src/main/java/Application.java @@ -0,0 +1,8 @@ +import racingcar.Game; + +public class Application { + public static void main(String[] args) { + Game game = new Game(); + game.start(); + } +} \ No newline at end of file diff --git a/racingcar/src/main/java/racingcar/Car.java b/racingcar/src/main/java/racingcar/Car.java new file mode 100644 index 00000000..118d5432 --- /dev/null +++ b/racingcar/src/main/java/racingcar/Car.java @@ -0,0 +1,31 @@ +package racingcar; + +import utils.RandomUtils; + +public class Car { + private static final int FIRST_DIGIT = 0; + private static final int LAST_DIGIT = 9; + private static final int POINT_DIGIT = 4; + + private final String name; + private int position = 0; + + public Car(String name) { + this.name = name; + } + + public String getName(){ + return name; + } + + public int getPosition(){ + return position; + } + + public void move() { + int randomNumber = RandomUtils.nextInt(FIRST_DIGIT, LAST_DIGIT); + if (randomNumber >= POINT_DIGIT) { + position++; + } + } +} \ No newline at end of file diff --git a/racingcar/src/main/java/racingcar/Cars.java b/racingcar/src/main/java/racingcar/Cars.java new file mode 100644 index 00000000..baf918fc --- /dev/null +++ b/racingcar/src/main/java/racingcar/Cars.java @@ -0,0 +1,28 @@ +package racingcar; + +import java.util.List; +import java.util.stream.Collectors; + +public class Cars { + private List cars; + + public Cars(List carNames) { + this.cars = carNames.stream() + .map(Car::new) + .collect(Collectors.toList()); + } + + public List run() { + for (Car car: cars) { + car.move(); + } + return cars; + } + + public Winner findWinner() { + return new Winner(cars ,cars.stream() + .mapToInt(Car::getPosition) + .max() + .getAsInt()); + } +} diff --git a/racingcar/src/main/java/racingcar/Game.java b/racingcar/src/main/java/racingcar/Game.java new file mode 100644 index 00000000..f11696a3 --- /dev/null +++ b/racingcar/src/main/java/racingcar/Game.java @@ -0,0 +1,21 @@ +package racingcar; + +import view.InputView; +import view.ResultView; + +public class Game { + + public void start() { + Cars cars = new Cars(InputView.inputCarName()); + int gameNumber = InputView.inputNumber(); + + ResultView.printResultTitle(); + + for (int i = 0; i < gameNumber; i++) { + ResultView.printResultPosition(cars.run()); + System.out.println(); + } + + ResultView.printWinners(cars.findWinner()); + } +} diff --git a/racingcar/src/main/java/racingcar/Winner.java b/racingcar/src/main/java/racingcar/Winner.java new file mode 100644 index 00000000..06573ec7 --- /dev/null +++ b/racingcar/src/main/java/racingcar/Winner.java @@ -0,0 +1,24 @@ +package racingcar; + +import java.util.ArrayList; +import java.util.List; + +public class Winner { + private List cars; + private int maxPosition; + + public Winner(List cars, int maxPosition) { + this.cars = cars; + this.maxPosition = maxPosition; + } + + public List getWinnersName() { + List winnerNames = new ArrayList<>(); + for (int i = 0; i < cars.size(); i++) { + if(cars.get(i).getPosition() == maxPosition){ + winnerNames.add(cars.get(i).getName()); + } + } + return winnerNames; + } +} diff --git a/racingcar/src/main/java/utils/RandomUtils.java b/racingcar/src/main/java/utils/RandomUtils.java new file mode 100644 index 00000000..f951aecf --- /dev/null +++ b/racingcar/src/main/java/utils/RandomUtils.java @@ -0,0 +1,27 @@ +package utils; + +import java.util.Random; + +public class RandomUtils { + private static final Random RANDOM = new Random(); + + private RandomUtils() { + + } + + public static int nextInt(final int startInclusive, final int endInclusive) { + if (startInclusive > endInclusive) { + throw new IllegalArgumentException("[ERROR] Random 값이 잘못 되었습니다."); + } + + if (startInclusive < 0) { + throw new IllegalArgumentException("[ERROR] Random 값이 잘못 되었습니다."); + } + + if (startInclusive == endInclusive) { + return startInclusive; + } + + return startInclusive + RANDOM.nextInt(endInclusive - startInclusive + 1); + } +} \ No newline at end of file diff --git a/racingcar/src/main/java/view/InputView.java b/racingcar/src/main/java/view/InputView.java new file mode 100644 index 00000000..21941bd5 --- /dev/null +++ b/racingcar/src/main/java/view/InputView.java @@ -0,0 +1,26 @@ +package view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private static final Scanner SCANNER = new Scanner(System.in); + + private InputView() { } + + public static List inputCarName(){ + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + return Arrays.asList(SCANNER.nextLine().split(",")); + } + + public static int inputNumber(){ + System.out.println("시도할 회수는 몇회인가요?"); + try { + int number = SCANNER.nextInt(); + return number; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("[ERROR] 숫자가 아닌 문자를 입력하셨습니다.\n"); + } + } +} \ No newline at end of file diff --git a/racingcar/src/main/java/view/ResultView.java b/racingcar/src/main/java/view/ResultView.java new file mode 100644 index 00000000..03bf18d1 --- /dev/null +++ b/racingcar/src/main/java/view/ResultView.java @@ -0,0 +1,35 @@ +package view; + +import racingcar.Car; +import racingcar.Winner; + +import java.util.List; + +public class ResultView { + public static final String Bar = "-"; + + private ResultView() { } + + public static void printResultTitle(){ + System.out.println(); + System.out.println("실행 결과"); + } + + public static void printResultPosition(List cars){ + for(Car car : cars) { + printCarPosition(car); + } + } + + public static void printCarPosition(Car car) { + System.out.print(car.getName() + " : "); + for (int i = 0; i < car.getPosition(); i++) { + System.out.print(Bar); + } + System.out.println(); + } + + public static void printWinners(Winner winner) { + System.out.println("최종 우승자: " + winner.getWinnersName()); + } +} diff --git "a/\353\224\224\353\257\270\355\204\260 \353\262\225\354\271\231.md" "b/\353\224\224\353\257\270\355\204\260 \353\262\225\354\271\231.md" new file mode 100644 index 00000000..e7f6b853 --- /dev/null +++ "b/\353\224\224\353\257\270\355\204\260 \353\262\225\354\271\231.md" @@ -0,0 +1,81 @@ +# 디미터 법칙 +> "Object-Oriented Programming: An Objective Sense of Style"에서 처음으로 소개된 개념으로 이 글의 저자들은 디미터라는 이름의 프로젝트를 진행하던 도중 객체들의 협력 경로를 제한하면 결합도를 효과적으로 낮출 수 있다는 사실을 발견했다. + +## 구조 은닉 설계 +간단하게 말하면, 긴 객체 구조의 경로를 따라 멀리 떨어져 있는 간접적인(낯선) 객체에 메시지를 보내는(또는 이야기하는) 설계는 피하라는 것이다. 이러한 설계는 일반적으로 불안정한 지점으로, 객체 구조의 변화에 부서지기 쉽다. + + +## Don't Talk to Strangers (낯선 이에게 말하지 마라) +Don't Talk to Strangers 패턴은 메소드 내에서 어떤 객체에 메시지를 보내야 하는가에 대한 제약을 가한다. 메소드 안에서 다음의 객체들에게만 메시지를 보내야 한다. + +1. this(또는 self) 객체 +2. 메소드의 매개변수 +3. this의 속성 +4. this의 속성인 컬렉션의 요소 +5. 메소드 내에서 생성된 객체 + +좀 더 정확히 표현하자면, 디미터 법칙은 "클래스 C의 메서드 f는 다음과 같은 객체의 메서드만 호출해야 한다"고 주장한다. +- 클래스 C +- f가 생성한 객체 +- f 인수로 넘어온 객체 +- C 인스턴스 변수에 저장된 객체 + +하지만 위 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안 된다. 다시 말해, 낯선 사람은 경계하고 친구랑만 놀라는 의미다. + +디미터 법칙은 객체의 모든 메소드는 다음에 해당하는 메소드만을 호출해야 한다고 말한다. +1. 객체 자신의 메소드 +2. 메소드의 매개변수로 넘어온 인자의 메소드 +3. 메소드 내부에서 생성 된 객체의 메소드 +4. 메소드가 포함하고 있는 객체의 메소드 +```java +class A { + private B b; + public setA(B b) { + b = b; + } + public myMethod(OtherObject other) { + // ... + } + /* 디미터의 법칙을 잘 따른 예 */ + public okLawOfDemeter(Paramemter param) { + myMethod(); // 자신의 메소드 + b.method(); // 자신의 멤버의 메소드 + Local local = new Local(); + local.method(); // 직접 생성한 객체의 메소드 + param.method(); // 메소드의 인자로 넘어온 메소드 + } + /* 디미터의 법칙을 어긴 예 */ + public violateLawOfDemeter(Paramemter param) { + C c = param.getC(); + c.method(); // 인자로 받은 객체에서의 호출. + param.getC().method(); // 위와 같음. + } +} + +``` +## 디미터 법칙을 위반한 코드 (기차 충돌) +```java +object.getChild().getContent().getItem().getTitle(); +``` +getter가 줄줄이 이어진 모습이 기차와 닮아서 열차 전복, 기차 충돌(train wreck) 이라는 단어로 표현하기도 한다. + +원거리의 간접적인 객체에 메시지를 보내기 위하여(즉, 원거리의 낯선 사람에게 이야기하기 위해서) 객체의 연결 경로를 따라 더 멀리 순회한다. 이러한 설계는 객체들이 어떻게 연결되어 있는지를 나타내는 특정한 구조와 결합된다. ***프로그램 순회의 경로가 길어질수록 프로그램은 더 불안정해진다.*** + +## 주의사항 +1. 자료구조라면 디미터 법칙을 거론할 필요가 없다. +2. 하나의 .을 강제하는 규칙이 아니다. +```java +IntStream.of(1, 15, 20, 3, 9) + .filter(x -> x > 10) + .distinct() + .count(); +``` +위 코드에서 of, filter, distinct 메서드는 모두 IntStream이라는 동일한 클래스의 인스턴스를 반환한다. 즉, 이들은 IntStream의 인스턴스를 또다른 IntStream의 인스턴스로 변환한다. + +따라서 이 코드는 디미터 법칙을 위반하지 않는다. +디미터 법칙은 결합도와 관련된 것이며, 이 결합도가 문제가 되는 것은 객체의 내부 구조가 외부로 노출되는 경우로 한정된다. + +## Reference +- [디미터 법칙](https://johngrib.github.io/wiki/law-of-demeter/) +- [[클린코드] 6장 객체와 자료구조](https://namget.tistory.com/entry/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-6%EC%9E%A5-%EA%B0%9D%EC%B2%B4%EC%99%80-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0) + diff --git "a/\354\235\274\352\270\211 \354\273\254\353\240\211\354\205\230.md" "b/\354\235\274\352\270\211 \354\273\254\353\240\211\354\205\230.md" new file mode 100644 index 00000000..f03af602 --- /dev/null +++ "b/\354\235\274\352\270\211 \354\273\254\353\240\211\354\205\230.md" @@ -0,0 +1,275 @@ +# 일급 콜렉션 +> Collection을 Wrapping하면서, Wrapping한 Collection 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라 한다. + +먼저 ***Collection을 Wrapping한다***의 의미는 아래 코드를 +```java +public class Person { + private String name; + private List cars; + // ... +} + +public class Car { + private String name; + private String oil; + // ... +} +``` +다음과 같이 바꾸는 것을 말한다. +```java +public class Person { + private String name; + private Cars cars; + // ... +} + +// List cars를 Wrapping +// 일급 컬렉션 +public class Cars { + // 멤버변수가 하나 밖에 없다!! + private List cars; + // ... +} + +public class Car { + private String name; + private String oil; + // ... +} +``` +위의 코드에서 봤듯이 일급 컬렉션은 그(List\ cars) 외 다른 멤버 변수가 없다. + +## 사용 이유 +GS편의점에 아이스크림을 팔고 있다. +```java +// GSConvenienceStore.class +public class GSConvenienceStore { + // 편의점에는 여러 개의 아이스크림을 팔고 있을 것이다. + private List iceCreams; + + public GSConvenienceStore(List iceCreams) { + this.iceCreams = iceCreams; + } + ... +} + +// IceCream.class +public class IceCream { + private String name; + ... +} +``` +특이하게도 해당 편의점은 아이스크림의 종류를 10가지 이상 넘으면 안 되는 검증이 필요할 것이다. +```java +// GSConvenienceStore.class +public class GSConvenienceStore { + private List iceCreams; + + public GSConvenienceStore(List iceCreams) { + validateSize(iceCreams) + this.iceCreams = iceCreams; + } + + private void validateSize(List iceCreams) { + if (iceCreams.size() >= 10) { + new throw IllegalArgumentException("아이스크림은 10개 이상의 종류를 팔지 않습니다.") + } + } + // ... +} +``` + +여기서 문제는 만약 아이스크림뿐만 아니라 과자, 라면 등 여러가지가 있다고 가정하였을 때 +- 모든 검증을 ***GSConvenienceStore class***에서 할 것인가? +- 만약 ***CUConvenienceStore class***에서도 동일한 것을 판다면 ***GSConvenienceStore class***에서 했던 검증을 또 사용할 것인가? + +List iceCreams의 원소 중에서 하나를 ***find***하는 메서드를 만든다고 가정해보자. +- ***GSConvenienceStore class***와 ***CUConvenienceStore class*** 같은 메서드(find)를 두번 구현할 것인가? + +```java +// GSConvenienceStore.class +public class GSConvenienceStore { + private List iceCreams; + // ... + public IceCream find(String name) { + return iceCreams.stream() + .filter(iceCream::isSameName) + .findFirst() + .orElseThrow(RuntimeException::new) + } + // ... +} + +// CUConvenienceStore.class +public class CUConvenienceStore { + private List iceCreams; + // ... + public IceCream find(String name) { + return iceCreams.stream() + .filter(iceCream::isSameName) + .findFirst() + .orElseThrow(RuntimeException::new) + } + // ... +} +``` +이럴 경우 편의점 ***class의 역할***이 무거워지고, ***중복코드***가 많아진다. +이것을 해결해주는 것이 ***일급컬렉션***이다. +***상태와 행위***를 각각 관리할 수 있다. +아이스크림을 일급 컬렉션으로 만들어 보았다. +```java +// IceCream.class +public class IceCreams { + private List iceCreams; + + public IceCreams(List iceCreams) { + validateSize(iceCreams) + this.iceCreams = iceCreams + } + + private void validateSize(List iceCreams) { + if (iceCreams.size() >= 10) { + new throw IllegalArgumentException("아이스크림은 10개 이상의 종류를 팔지않습니다.") + } + } + + public IceCream find(String name) { + return iceCreams.stream() + .filter(iceCream::isSameName) + .findFirst() + .orElseThrow(RuntimeException::new) + } + // ... +} +``` +그렇다면 편의점 class는 어떻게 달라졌을까? +```java +// GSConvenienceStore.class +public class GSConvenienceStore { + private IceCreams iceCreams; + + public GSConvenienceStore(IceCreams iceCreams) { + this.iceCreams = iceCreams; + } + + public IceCream find(String name) { + return iceCreams.find(name); + } + // ... +} + +// CUConvenienceStore.class +public class CUConvenienceStore { + private IceCreams iceCreams; + + public CUConvenienceStore(IceCreams iceCreams) { + this.iceCreams = iceCreams; + } + + public IceCream find(String name) { + return iceCreams.find(name); + } + // ... +} +``` +이로써 과자랑 라면 등이 생겨도 과자의 일급 컬렉션과 라면의 일급 컬렉션이 해줄 것이다. +그리고 편의점 class가 했던 역할을 아이스크림, 과자, 라면 등 각각에게 위임하여 상태와 로직을 관리할 것이다. + +정리하자면 ***일급 컬렉션***을 사용하면 ***상태와 로직을 따로 관리***할 수 있기 때문에 로직이 사용되는 ***클래스의 부담***을 줄일 수 있고, ***중복 코드***를 줄일 수 있다. + +## 불변성을 보장할 필요가 없는 이유 +밑의 코드와 같이 setter를 구현하지 않으면 불변 컬렉션이 된다는 글을 많이 보았을 것이다. +```java +public class Lotto { + private final List lotto; + // ... + public List getLotto() { + return lotto; + } +} +``` +하지만 setter를 사용하지 않더라도 Lotto 안에 있는 lotto 변수에 변화를 줄 수 있다. +```java +public class Lotto { + private final List lotto; + + public Lotto(List lotto) { + this.lotto = lotto; + } + + public List getLotto() { + return lotto; + } +} + +public class LottoNumber { + private final int lottoNumber; + + public LottoNumber(int lottoNumber) { + this.lottoNumber = lottoNumber; + } + + // toString()은 로그를 찍기 위함이다. + @Override + public String toString() { + return "LottoNumber{" + + "lottoNumber=" + lottoNumber + + '}'; + } +} +``` +위와 같은 코드가 있다고 가정하였을 때, +```java +@Test +public void lotto_변화_테스트() { + List lottoNumbers = new ArrayList<>(); + lottoNumbers.add(new LottoNumber(1)); + Lotto lotto = new Lotto(lottoNumbers); + lottoNumbers.add(new LottoNumber(2)); +} +``` +이러한 상황에서 lotto를 get하였을 때 ```[LottoNumber{lottoNumber=1}, LottoNumber{lottoNumber=2}]``` 이러한 값을 가지고 있는 것을 알 수 있다. +***lottoNumbers***와 ***lotto class의 멤버변수***와 주소값이 같기 때문에 영향을 받는다. +Lotto class의 맴버변수인 lotto가 파라미터로 받은 lottoNumbers의 영향을 받지 않기 위해서는 다음과 같이 수정하면 된다. +```java +public class Lotto { + private final List lotto; + + public Lotto(List lotto) { + this.lotto = new ArrayList<>(lotto); + } + + public List getLotto() { + return lotto; + } +} +``` +이렇게 수정하면 멤버변수에 저장되는 주소값을 재할당하기 때문에 영향을 받지 않는다. +```java +@Test +public void lotto_변화_테스트() { + List lottoNumbers = new ArrayList<>(); + lottoNumbers.add(new LottoNumber(1)); + Lotto lotto = new Lotto(lottoNumbers); + lotto.getLotto().add(new LottoNumber(2)); +} +``` +하지만 이러한 상황에도 위와 같은 결과 값이 나온다. +이를 해결하는 방법으로 ***unmodifiableList*** 사용한다. +```java +public class Lotto { + private final List lotto; + + public Lotto(List lotto) { + this.lotto = new ArrayList<>(lotto); + } + + public List getLotto() { + return Collections.unmodifiableList(lotto); + } +} +``` +***unmodifiableList***를 사용하면 lotto는 불변이 되고, getter로 return해서 사용될 때 변경이 불가능하다. + +## Reference +- [일급 컬렉션](https://velog.io/@tigger/%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98) \ No newline at end of file