JPA(Java Persistence API) 란? 

자바 진영에서 ORM(Object-Relational Mapping) 표준 기술로 Hibernate, Spring JPA, EcliplseLink 등 과 같은 구현체가 있고 이것의 표준 인터페이스가 JPA이다.

 

+ ORM(Object-Relational Mapping)이란 자바의 객체와 관계형 DB를 맵핑하는 것으로 DB의 특정 테이블이 자바의 객체로 맵핑되어 SQL문을 일일이 작성하지 않고 객체로 구현할 수 있도록 하는 프레임워크. DB에서 쿼리로 해야할 것을 스프링에서 접근하여 데이터를 다를 수 있게 구현한 것

 

사용 예제)

@Entity // DB 테이블과 연결될 객체 설정해주는 어노테이션
public class User {

	// ID가 자동으로 생성
    @ID  	
    @GeneratedValue(strategy = GenerationType.AUTO)	 // 자동 증가 auto_increment 역할
    private Long id;
    
    // 컬럼 설정
    @Column(nullable = false; unique = true) //nullable : null 허용, unique : 유일성
    private String username;
    
    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private UserRoleEnum role;

	....
}

 

테이블 관계 선언) 

Entity 클래스 필드 위에 연관관계 어노테이션을 설정해 주는 것으로 연관관계가 형성된다. 

 

+ @가 붙어있는 코드를  Annotation이라고 한다.

용도 : 컴파일러에게 문법 에러 체크하도록 정보 제공, 개발툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보제공, 런타임시 특정 기능을 실행하도록 정보를 제공하는 기능을 하며 자바에서는 @ (at sign)을 앞에 붙여서 사용

 

- 연관관계 어노테이션

일대다 (1:N) @OneToMany
다대일(N:1) @ManyToOne
일대일(1:1) @OneToOne
다대다(N:N) @ManyToMany

 

실습 예제) 

- Entity 생성  : 일반 클래스로 생성

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

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

@NoArgsConstructor  // 기본 생성자 만들어주는 어노테이션
@Getter             // 값을 가져오는 어노테이션
@Entity             // 테이블 생성하는 어노테이션
public class Member {

    @Id // ID생성
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 키 생성 전략 선택   : IDENTITY 기본키 생성을 데이터베이스에 위임
    private Long id;                                    // 키 이름과 타입을 변수로 선언

    @Column(nullable = false)                           // null값 허용 옵션
    private String memberName;

    // @OneToMany 1:N,              테이블간의 연관관계 설정
    // mappedBy = 컬럼명,            어떤 컬럼에 맵핑될 건지 즉, 연관관계의 주인이 누군지 설정
    // fetch = FetchType.EAGER      글로벌 패치전략, EAGER은 즉시로딩, LAZY는 지연로딩
    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
    private List<Orders> orders = new ArrayList<>();            // 여러오더를 받아야하므로 list 형식으로 하며 초기값을 주기위해 new

    public Member(String memberName) {
        this.memberName = memberName;       // 이 연관관계의 연결될 키를 설정
    }
}

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Entity
@Getter
@NoArgsConstructor
public class Orders {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "food_id")   // 외래키 설정
    private Food food;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;

    public Orders(Food food, Member member) { //두개의 외래키에 조인 대상을 설정
        this.food = food;
        this.member = member;
    }
}

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

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

@Getter
@Entity
@NoArgsConstructor
public class Food {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String foodName;
    @Column(nullable = false)
    private int price;

    @OneToMany(mappedBy = "food",fetch = FetchType.EAGER)
    private List<Orders> orders = new ArrayList<>();

    public Food(String foodName, int price) {
        this.foodName = foodName;
        this.price = price;
    }
}

 

- repository 생성 : 인터페이스로 생성

아래 세가지는 테이블 이름만 다를 뿐 ID타입 마저도 모두 동일

package com.example.demo.repository;

import com.example.demo.entity.Food;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FoodRepository extends JpaRepository<Food, Long> { // extend의 Food는 연결할 테이블 이름, 뒤에 Long은 ID의 타입
}

package com.example.demo.repository;

import com.example.demo.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {
}

package com.example.demo.repository;

import com.example.demo.entity.Orders;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrdersRepository extends JpaRepository<Orders, Long> {
}

 

- ApplicationRunner 상속 클래스 : 앞에서 DB를 구성하고 사용할 준비를 하는 부분이라면 이 부분이 실질적으로 진짜 데이터를 조회하고 수정하고 사용하는 부분.

package com.example.demo;

import com.example.demo.entity.*;
import com.example.demo.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

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

@Component
@RequiredArgsConstructor
public class Restaurant implements ApplicationRunner {

    private final FoodRepository foodRepository;
    private final OrdersRepository ordersRepository;
    private final MemberRepository memberRepository;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        List<Food> foods = new ArrayList<>();
        Food food1 = new Food("후라이드", 10000);
        foods.add(food1);
        Food food2 = new Food("양념치킨", 12000);
        foods.add(food2);
        Food food3 = new Food("반반치킨", 13000);
        foods.add(food3);
        Food food4 = new Food("고구마피자", 9000);
        foods.add(food4);
        Food food5 = new Food("아보카도피자", 110000);
        foods.add(food5);
        foodRepository.saveAll(foods);

        List<Member> members = new ArrayList<>();
        Member member1 = new Member("삼식이");
        members.add(member1);
        Member member2 = new Member("먹깨비");
        members.add(member2);
        memberRepository.saveAll(members);

        System.out.println("==================================================================");

        System.out.println("Member 데이터");
        List<Member> findMembers = memberRepository.findAll();
        for (Member findMember : findMembers) {
            System.out.println("findMember = " + findMember.getMemberName());
        }

        System.out.println("==================================================================");

        System.out.println("Food 데이터");
        List<Food> findFoods = foodRepository.findAll();
        for (Food findFood : findFoods) {
            System.out.println("findFood = " + findFood.getFoodName());
        }

        List<Orders> ordersList = new ArrayList<>();
        Orders orders1 = new Orders(findFoods.get(0), findMembers.get(0));
        ordersList.add(orders1);
        Orders orders2 = new Orders(findFoods.get(3), findMembers.get(1));
        ordersList.add(orders2);
        Orders orders3 = new Orders(findFoods.get(4), findMembers.get(1));
        ordersList.add(orders3);
        Orders orders4 = new Orders(findFoods.get(2), findMembers.get(0));
        ordersList.add(orders4);
        Orders orders5 = new Orders(findFoods.get(2), findMembers.get(0));
        ordersList.add(orders5);
        Orders orders6 = new Orders(findFoods.get(1), findMembers.get(1));
        ordersList.add(orders6);
        Orders orders7 = new Orders(findFoods.get(1), findMembers.get(0));
        ordersList.add(orders7);
        Orders orders8 = new Orders(findFoods.get(3), findMembers.get(1));
        ordersList.add(orders8);
        ordersRepository.saveAll(ordersList);

        System.out.println("==================================================================");
        int num = 1;

        System.out.println("Orders 데이터");
        List<Orders> orderList = ordersRepository.findAll();

        for (Orders orders : orderList) {
            System.out.println(num);
            System.out.println("주문한 사람 = " + orders.getMember().getMemberName());
            System.out.println("주문한 음식 = " + orders.getFood().getFoodName());
            num++;
        }

        System.out.println("==================================================================");
        System.out.println("삼식이 주문한 음식");
        Member samsik = memberRepository.findById(1L).orElseThrow(
                ()->new RuntimeException("없음")
        );

        num = 1;
        for (Orders orders : samsik.getOrders()) {
            System.out.println(num);
            System.out.println("주문한 음식 = " + orders.getFood().getFoodName());
            System.out.println("주문한 음식 가격 = " + orders.getFood().getPrice());
            num++;
        }


        System.out.println("==================================================================");
        System.out.println("아보카도피자 주문한 사람");
        Food abocado = foodRepository.findById(5L).orElseThrow(
                ()->new RuntimeException("없음")
        );

        for (Orders order : abocado.getOrders()) {
            System.out.println("주문한 사람 = " + order.getMember().getMemberName());
        }




    }
}

 

 

 

실제 저장된 걸 확인하려면 http://localhost:8080/h2-console/ 에 들어가 설정해준 user로 적어주고

JDBC URL 부분에 위에 엔터티와 리포지토리를 생성하며 실행한 로그에 나와있는 url을 적용해 수정주어 connect하면된다. 

연결이 성공하면 

 

위처럼 확인할 수 있다.

 

 


엔터티 상속 ) 

엔터티 클래스를 다른 엔터티 클래스에 상속시켜 하나의 엔터티를 여러곳에서 공통적으로 사용할 수 있다. 

 

 

Spring Data JPA  란? )

JPA를 편하게 사용하기 위해, 스프링에서 JPA를 *Wrapping한 *Dependency

 

+ Wrapping / 래핑 : 활동범위를 설정하고 좀더 중요한 다른 프로그램의 실행을 가능하게 포장한 것을 말한다. 

+ Dependency가 무슨 뜻인질 모르겠다.

 

 

 


Spring Data JPA  제공하는 기능) 

 

  • SAVE : 데이터 삽입
리포지토리이름.save(변수);			// 1개 insert
리포지토리이름.saveAll(리스트변수)		// 여러개 insert
  • FIND : 데이터 조회
리포지토리이름.find();			// 1개 찾기, 조건은?
리포지토리이름.findAll()		// 모두 찾기, 조건은?
  • COUNT : 데이터 개수 조회
리포지토리이름.count();  // 조건은?
  • DELETE : 데이터 삭제
리포지토리이름.delete(변수명);
  • ID외의 필드값에 대한 추가 기능은 interface에서 선언해주면 Spring Data JPA가 대신해주어 메소드로 사용이 가능해진다. 아래처럼 interface에 선언해주면 
public interface ProductRepository extends JpaRepository<Product, Long> {
	// 회원 ID 로 등록된 상품들 조회
	List<Product> findAllByUserId(Long userId);

	// 상품명이 title 인 관심상품 1개 조회
	Product findByTitle(String title);

	// 상품명에 word 가 포함된 모든 상품들 조회
	List<Product> findAllByTitleContaining(String word);

	// 최저가가 fromPrice ~ toPrice 인 모든 상품들을 조회
	List<Product> findAllByLpriceBetween(int fromPrice, int toPrice);
}

ApplicationRunner을 상속한 클래스에서 여기서 정의한 함수를 사용할 수 있다.

 

 

 

 

+ Spring Data JPA 추가기능 구현방법에 관한 공식 문서

 

Spring Data JPA - Reference Documentation

Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

 

 

 

 


Tip
+ 클래스 이름에 커서를 놓고 F4를 누르면 해당 클래스 내용을 볼 수 있다.

 

 

1-2 SpringBoot 및 서버 이해

 

소프트웨어 디자인 패턴 이란? ) 

소프트웨어 디자인에서 특정 문맥에서 공통적으로 발생하는 문제에 대해 재사용 가능한 해결책이다. 

다른 상황에 맞게 사용될 수 있는 문제들을 해결하는 데에 쓰이는 서술이나 템플릿이며 프로그래머가 어플리케이션이나 시스템을 디자인할 때 공통된 문제들을 해결하는데 쓰이는 형식화 된 좋은 관행이다.

 

 

Software Architecture Patterns

Chapter 1. Layered Architecture The most common architecture pattern is the layered architecture pattern, otherwise known as the n-tier architecture pattern. This pattern is the de facto standard for most … - Selection from Software Architecture Pattern

www.oreilly.com

 


레이어드 아키텍처 패턴 ) 

서버를 총 세가지 부분으로 나누는 패턴으로 Presentation 계층, Domain (Business or Service) 계층, Data Access (Persistence) 계층으로 나눈다. 

 

  • Presentation 계층 : 사용자와 상호 작용 처리 계층

CLI ( Command-Line Interface / 명령 줄 인터페이스 ) , HTTP요청 및 HTML 렌더링에 대해 알고 있는 웹계층으로 MVC (Model / View / Controller) 도 이 계층에 속함.

스프링에서는 @Controller 어노테이션을 사용해서 표현

 

+ Annotation 용도 : 컴파일러에게 문법 에러 체크하도록 정보 제공, 개발툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보제공, 런타임시 특정 기능을 실행하도록 정보를 제공하는 기능을 하며 자바에서는 @ (at sign)을 앞에 붙여서 사용

 

  • Domain (Business or Service) 계층 : 서비스/시스템의 핵심로직으로 유효성 검사, 계산을 포함하는 Business 논리계층

애플리케이션이 수행해야하는 도메인과 관련된 작업을 담당. 입력/저장된 데이터를 기반으로 계산, Presentation 계층이 받은 데이터 유효성 검사, 어떤 Data Access를 선택할 지 결정

스프링에서는 @Service 어노테이션을 사용해서 표현

 

  • Data Access (Persistence) 계층 : DAO 계층

Database / Message Queue / 외부 API와의 통신 등 처리

데이터베이스 또는 원격 서비스에서 영구 데이터를 관리하는 방법을 분류하는 데이터 접근 계층이다. 

스프링에서는 @Repository 어노테이션을 사용해서 표현

 

+ DAO (Data Access Object) 는 데이터 계층에서 데이터베이스로 접근하는 로직을 담당한다

 

아주 쉽게 비유해주신 그림

 

 

 


공부하다 보면 기본 자바와 스프링에서 문법이 다른부분이 있을것이다

이부분에 대해 메모하고 의아해 하는 습관을 가지면 IOC/DI 등 설계 철학 등을 배우는 데 큰 도움이 된다. 

 

+ IOC / DI :스프링의 핵심 프로그래밍 모델로, 오브젝트의 생명주기와 의존 관계에 대한 프로그래밍 모델이다. 유연하고 확장성이 뛰어난 코드를 만들 수 있게 도와주는 객체지향 설계 원칙과 디자인 패턴의 핵심 원리를 담고 있다. 

스프링이 제공하는 모든 기술과 API, 컨테이너 모두 IoC/DI 방식으로 작성되어 있다. 

 


스프링 / 스프링부트 를 사용하는 이유 )

단순 반복작업 부분이 많았던 Controller와 Repository쪽을 개발 관점에서 쉽고 편하게 처리해줘 가장 중요한 비즈니스 로직인 Service 레이어에 집중 할 수 있도록 해줬기 때문에 스프링을 사용한다. 

 

1-1 웹 동작 방식 이해하기

 

IP주소 란? ) 

거대한 네트워크망에서 컴퓨터를 식별하기 위한 위치 주소.

네트워크 상의 데이터 송/수신은 이 주소를 기준으로 이루어지고 있다.

 


브라우저 란? ) 

웹페이지, 이미지, 비디오 등의 컨테츠를 송/수신하고 표현해주는 소프트웨어.

브라우저가 하는 가장 큰일 또한 컨텐츠 송/수신, 컨텐츠 표현이라고 할 수 있다.

 

 


DNS (Domain Name Server) 란? )

요청을 받아줄 서버의 ip주소와 같은 것들과 도메인 이름을 중개해주는 서버가 있는데 이것이 바로 DNS.

그래서 우리가 사이트를 찾아 들어갈때 ip주소를 쓰는 것이 아니라 사이트 이름의 주소를 쓰는게 가능한 것이다. 

 

 


HTTP (Hypertext Transfer Protocol) 란? )

서버 개발자 입장에서 요청을 받기 위한 프로그래밍을 짜야할 것이다. 그런데 요청을 형식없이 해온다면 그걸 과연 다 처리할 수 있을까? 불가능 하기 때문에 요청을 하는 메세지 송신자와 수신자가 서로 이런 형식에 대한 약속을 하게 되었다. 

메세지의 송/수신자가 서로 필요한 요청과 응답을 할 수 있도록 미리 규약을 정해 놓은걸 "프로토콜" 이라고 하며, 웹 상에서는 HTTP 라는 프로토콜을 통해 데이터를 송/수신 하고 있다. 

 

 


API (Application Programming Interface) 란? )

다른 소프트웨어 시스템과 통신하기 위해 따라야 하는 규칙을 정의한 것으로 서버의 창구라 불리운다.

또한 웹 API의 경우에는 클라이언트와 웹 리소스 사이의 게이트 웨이라 할 수 있다. 

개발자는 다른 애플리케이션이 프로그래밍 방식으로 애플리케이션과 통신할 수 있도록 API를 표시하거나 생성해야한다.

감이 잘 안온다면 서로 다른 어플리케이션 간에 약속한 방식으로 요청하면 요청에 따라 결정되어 있는 응답을 돌려주는 약속이라고 생각하면 좋을 듯하다.

 

 


Interface 란? )

서로 다른 두개의 시스템, 장치 사이에서 정보나 신호를 주고 받는 경우의 접점이나 경계면이다. 즉 사용자가 기기를 쉽게 동작시키는데 도움을 주는 시스템을 의미한다. 컴퓨팅에서 컴퓨팅 시스템 끼리 정보를 교환하는 경계 뿐만 아니라, 소프트웨어, 컴퓨터 하드웨어, 주변기기, 사람간에도 이루어질 수 있고 서로 복합적으로 섞어 이루어 질 수도 있다. 

 

 


RESTful API 란? ) 

  • REST

Representational State Transfer의 약자로 API 작동 방식에 대한 조건을 부과하는 소프트웨어 아키텍처이다. 

네트워크 통신을 관리하기 위한 지침으로 만들어졌는데 REST시반 아키텍처를 사용하여 대규모의 고성능 통신을 안정적으로 지원할 수 있다. 

 

  • RESTful API

REST 아키텍처 스타일을 따르는 API를 REST API라고 한다. 그리고 REST 아키텍처를 구현하는 웹 서비스를 RESTful 웹 서비스라고 한다. 개발한 서버에 API가 적절하게 http를 준수하며 잘 설계 되어 있으면 RESTful 하게 설계 되어 있다.

 

-고유 리소스 식별자 

서버는 고유한 리소스 식별자로 각 리소스를 식별하는 데 REST 서비스 경우에는 일반적으로 URL을 사용해 리소스 식별을 수행한다

 

-URL (Uniform Resource Locator)

url은 리소스에 대한 경로를 지정한다. url은 요청 엔드포인트라고도 하며 클라이언트가 요구하는 사항을 서버에 명확하게 지정한다.

 

-HTTP Method

HTTP 메소드는 리소스에 수행해야 하는 작업을 서버에 알려주는 역할을 하는데 다음 4가지가 일반적이다

GET 클라이언트는 Get을 사용해 서버의 지정된 URL에 있는 리소스에 접근 한다. 
Get요청을 캐싱하고 RESTful API요청에 파라미터를 넣어 전송하여 전송 전 데이터를 필터링하도록 서버에 지시할 수 있다.
POST 클라이언트는 POST를 이용하여 서버에 데이터를 전송한다. 이는 요청과 함께 데이터 표현이 포함된다. 동일한 POST 요청을 여러번 전송하면 동일한 리소스를 여러번 생성하는 부작용이 있다.
PUT 클라이언트는 PUT을 이용하여 기존 리소스를 업데이트 한다. POST와 달리, RESTful 웹 서비스에서 동일한 PUT 요청을 여러번 전송해도 결과는 동일하다.
DELETE 지정된 리소스를 삭제한다. 

 

 

 

RESTful API란 무엇인가? - RESTful API 초보자 가이드 - AWS

Amazon API Gateway는 어떤 규모에서든 개발자가 API를 손쉽게 생성, 게시, 유지 관리, 모니터링 및 보안 유지할 수 있도록 하는 완전관리형 서비스입니다. API Gateway를 사용하면 실시간 양방향 통신 애

aws.amazon.com


데이터베이스 (DataBase)

통합하여 관리되는 데이터의 집합체를 의미한다. 또한 많은 데이터를 저장하기 위한 것 보다는 효율적으로 성능 좋게 데이터를 다루기 위해서 사용한다. 

 

 


 

결론적으로 서버 개발에서 많이 하는 일은 새로운 정보와 기존의 정보를 가지고 정해진 로직을 수행하는 일이다.

 

 

 

클래스에 대해 공부해서 다음과 같은 요구사항에 대한 Bus와 Taxi에 대한 클래스를 설계하고 생성하여 깃헙url로 제출

  1. 아래의 정보를 가지는 ‘Bus’ 클래스 모델링
    • 포함해야 할 정보
      1. 최대 승객수
      2. 현재 승객수
      3. 요금
      4. 버스 번호
      5. 주유량
      6. 현재 속도
      7. 상태
        1. 운행, 차고지 행
    • 기능
      1. 운행
      2. 버스 상태 변경
      3. 승객 탑승
      4. 속도 변경
    요구사항
    • 버스 번호
      • 버스 객체 생성시 번호는 고유값으로 생성되어야 합니다.
    • 버스 상태 변경
      • 버스 객체 생성시 최초 상태는 ‘운행’ 상태가 되며
      • 주유량이 떨어지거나, 운행을 종료할 때 ‘차고지행’ 상태로 변경 합니다.
      • 10미만일 경우 ‘주유가 필요하다’는 메시지를 출력해 주세요
    • 승객 탑승
      • 승객 탑승은 ‘최대 승객수’ 이하까지 가능하며 **‘운행 중’**인 상태의 버스만 가능합니다.
      • 탑승시 현재 승객수가 증가되어야 합니다.
    • 속도 변경
      • 주유 상태를 체크하고 주유량이 10 이상이어야 운행할 수 있습니다.
        • 경고메시지
          • 주유량을 확인해 주세요.
          • print문으로 출력
      • 변경할 속도를 입력 받아 현재 속도에 추가 하거나 뺄 수 있어야 합니다.
  2. 아래의 정보를 가지는 ‘Taxi’ 클래스 모델링
    • 포함해야 할 정보
      1. 택시 번호
      2. 주유량
      3. 현재속도
      4. 목적지
      5. 기본거리
      6. 목적지까지 거리
      7. 기본 요금
      8. 거리당 요금
      9. 상태 (운행 중, 일반, 운행불가)
    • 기능
      1. 운행시작
      2. 승객 탑승
      3. 속도 변경
      4. 거리당 요금 추가
      5. 요금 결제
    • 요구 사항
      • 운행 시작
        • 운행 시작전 주유상태를 체크 하고 주유량이 10 이상이어야 운행 가능
      • 승객탑승
        • 승객 탑승은 택시 상태가 ‘일반'일 때만 가능합니다.
        • 그 외 택시는 ‘탑승 불가’ 처리를 해주세요.
        • ‘일반’ 상태의 택시가 승객을 태우면 ‘운행 중’ 상태로 변경해 주세요
      • 속도 변경
        • 변경할 속도를 입력 받아 현재 속도에 추가 하거나 뺄 수 있어야 합니다.
      • 거리당 요금 추가
        • 기본 거리보다 먼 곳은 추가 요금이 붙습니다.
        • 기본 거리와 추가 요금은 자유롭게 산정해 주세요
      • 요금 결제
        • 최종 요금을 출력하는 것으로 합니다.

 

 


 

따로따로 클래스를 만들려다가 시나리오에서 상위클래스로 교통수단을 묶어 클래스를 만든걸 보고

상속 클래스를 사용해보고 싶어서 상위 클래스를 만들었다. 공통부분을 상위클래스에 두고 하위 클래스로 taxi와 bus를 나누어 만들었다. 그리고 실행은 또 별도의 main함수를 두어 실행하는 구조로 진행

 

상위 클래스인 Transportation ) 

package assignment_class_modeling;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class Transportaion {
    String num;		// 차량번호 고유번호
    int baseRate;       // 기본요금
    int refuelAmount;   // 주유량
    int nowSpeed;       // 지금 속력
    int stateNum;       // 지금 상태 0: 운행불가, 1: 운행가능, 2:제3의상태
    String state;

    // 생성자
    public Transportaion (int refuelAmount){
        this.refuelAmount = refuelAmount;
        stateNum = 1;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        Calendar dateTime = Calendar.getInstance();
        num = sdf.format(dateTime.getTime());
    }

    // 운행 시작
    public void run(){
        if(refuelAmount < 10){
            stateNum = 0;
        } else {
            stateNum = 1;
        }
    }

    // 상태변경 메소드 : 0: 운행불가, 1: 운행가능, 2:제3의상태
    public void stateChange(int x){
        if(x == 1){
            if(refuelAmount < 10){
                System.out.println("주유가 필요합니다");
                stateNum = 0;
            } else {
                stateNum = 1;
            }
        } else {
            stateNum = x;
        }
    }

    // 승객 탑승
    public void passengerRiding(){

    }

    // 속력변경 메소드 : Operator = '-','+'
    public void speedChange(char operator, int speed){
        if( stateNum != 0 && refuelAmount >= 10){
            if(operator == '+') nowSpeed += speed;
            if(operator == '-') nowSpeed -= speed;
        } else if (refuelAmount < 10){
            System.out.println("주유량을 확인해 주세요.");
        }
    }
}

 

하위클래스 1 : Bus ) 

package assignment_class_modeling;

public class Bus extends Transportaion {
    int maxPassengerCnt;		// 탑승가능 인원수
    int nowPassengerCnt;		// 현재 인원수
    int remainingSeat;			// 남은 자리
    String[] stateStr = {"차고지행","운행"};

    // 생성자
    public Bus(int refuelAmount){
        super(refuelAmount);
        state = stateStr[stateNum];

        maxPassengerCnt = 30;
        remainingSeat = 30;
        nowPassengerCnt = 0;
    }

    // 운행 시작
    public void run(){
        super.run();
        state = stateStr[stateNum];
    }

    // 상태변경 메소드 : 0: 운행불가, 1: 운행가능
    public void stateChange(int x){
        super.stateChange(x);
        state = stateStr[stateNum];
    }

    // 승객 탑승
    public void passengerRiding(int personCnt){
        if(stateNum == 1){
            if(maxPassengerCnt > (nowPassengerCnt + personCnt)){
                nowPassengerCnt += personCnt;
                remainingSeat = maxPassengerCnt - nowPassengerCnt;
            } else {
                System.out.println("최대 승객 수가 초과하여 탑승할 수 없습니다.");
            }
        }
    }

    // 속력변경 메소드 : Operator = '-','+'
    public void speedChange(char operator, int speed){
        super.speedChange(operator, speed);
    }

}

 

하위클래스 2 : Taxi ) 

package assignment_class_modeling;

public class Taxi extends Transportaion {
    String destination;
    int basDistance = 3; 		// 기본 거리 3km
    int distanceToDestination;		// 목적지까지 거리
    int perDistanceRate = 200;  	// 거리당 요금
    int sumDistanceRate;           	// 거리요금 총합
    String[] stateStr = {"운행불가", "일반", "운행중"};

    // 생성자
    public Taxi (int refuelAmount){
        super(refuelAmount);

        if(refuelAmount < 10){
            stateNum = 0;
        }
        state = stateStr[stateNum];

        baseRate = 3300;
    }

    // 운행 시작
    public void run(){
        super.run();
        state = stateStr[stateNum];
    }

    // 상태변경 메소드 : 0: 운행불가, 1: 운행가능, 2:제3의상태
    public void stateChange(int x){
        super.stateChange(x);
        state = stateStr[stateNum];
    }

    // 승객 탑승
    public void passengerRiding(){
        if(stateNum == 1){
            stateNum = 2;
            state = stateStr[stateNum];
        } else {
            System.out.println("탑승 불가 입니다");
        }
    }

    // 속력변경 메소드 : Operator = '-','+'
    public void speedChange(char operator, int speed){
        super.speedChange(operator, speed);
    }

    // 거리당 요금 추가 메소드
    public void plusPerRate(String destination, int distanceToDestination){
        this.destination = destination;
        System.out.println("목적지 : "+this.destination);
        if(distanceToDestination > basDistance){
            sumDistanceRate =  perDistanceRate = (distanceToDestination - basDistance) * perDistanceRate;
        }
    }

    // 최종 요금 출력 메소드
    public void finalRateCaculate (){
        int finalRate = baseRate + sumDistanceRate;
        System.out.println("최종 요금은 " + finalRate + "원 입니다");
    }
}

 

실행클래스 Main ) 

package assignment_class_modeling;

public class Main {
    public static void main(String[] args) {

        System.out.println("--------------------------------버스 객체 생성");
        Bus bus1 = new Bus(100);
        System.out.println("버스1 상태: " + bus1.state);
        System.out.println("버스1 번호: " + bus1.num);
        Bus bus2 = new Bus(5);
        System.out.println("버스2 상태: " + bus2.state);
        System.out.println("버스2 번호: " + bus2.num);

        System.out.println("--------------------------------버스 상태 변경");
        bus1.stateChange(0);
        System.out.println("버스 상태: " + bus1.state);
        bus1.run();
        System.out.println("run() 이후 버스상태: " + bus1.state);

        System.out.println("--------------------------------버스 승객 탑승");
        bus1.passengerRiding(13);
        System.out.println("현재 탑승자 수: " +bus1.nowPassengerCnt);
        System.out.println("현재 여유석 수: " +bus1.remainingSeat);
        bus1.passengerRiding(20);
        System.out.println("현재 탑승자 수: " +bus1.nowPassengerCnt);
        System.out.println("현재 여유석 수: " +bus1.remainingSeat);


        System.out.println("--------------------------------버스 속도 변경");
        bus1.speedChange('+', 100);
        System.out.println("버스 현재속력: " + bus1.nowSpeed);
        bus1.speedChange('-', 40);
        System.out.println("버스 현재속력: " + bus1.nowSpeed);

        System.out.println("--------------------------------택시 객체 생성");
        Taxi taxi = new Taxi(100);
        System.out.println("택시 상태: " + taxi.state);
        System.out.println("택시 번호: " + taxi.num);

        System.out.println("--------------------------------택시 상태 변경");
        taxi.stateChange(0);
        System.out.println("택시 상태: " + taxi.state);
        taxi.run();
        System.out.println("run() 이후 택시상태: " + taxi.state);
        taxi.stateChange(2);
        System.out.println("택시 상태: " + taxi.state);
        taxi.stateChange(1);
        System.out.println("택시 상태: " + taxi.state);

        System.out.println("--------------------------------택시 승객 탑승");
        taxi.passengerRiding();
        System.out.println("택시 상태: " + taxi.state);

        System.out.println("--------------------------------택시 속도 변경");
        taxi.speedChange('+', 150);
        System.out.println("택시 현재속력: " + taxi.nowSpeed);
        taxi.speedChange('-', 100);
        System.out.println("택시 현재속력: " + taxi.nowSpeed);

        System.out.println("--------------------------------택시 거리당 요금 추가");
        taxi.plusPerRate("서울", 74);
        System.out.println("목적지 " + taxi.destination + "까지 거리당 추가 요금은 " + taxi.sumDistanceRate + "원 입니다." );

        System.out.println("--------------------------------택시 최종 요금");
        taxi.finalRateCaculate();

        System.out.println("종료");
    }
}

 

 

깃헙 주소: 

 

GitHub - littlezero48/sparta_assignment: 항해99 숙제

항해99 숙제. Contribute to littlezero48/sparta_assignment development by creating an account on GitHub.

github.com

 

 

 

더 나은 개선을 위해 참고하라고 주신 링크들:

 

생각하라, 객체지향처럼 | 우아한형제들 기술블로그

{{item.name}} 2년차 쪼랩이가 객체지향을 처음으로 접하고 공부를 하면서 나름대로 정리해보았습니다. 시작 인사 안녕하세요. B2B시스템개발팀 김승영입니다. 먼저, 이 글은 지난 02월에 작성되어

techblog.woowahan.com

 

객체지향(OOP)과 절차적 프로그래밍(PP)

오늘은 프로그래밍에서 중요한 개념 중 하나인 객체지향 프로그래밍(Object Oriented Programming)과 절차적 프로그래밍(Procedure Programming)에 대해 알아보고자 합니다. 대개 객체지향 프로그래밍 언어를

st-lab.tistory.com

 

(Java) 21 - 객체 지향 예제 - 학생, 버스, 지하철 프로그램

객체 지향 프로그래밍에 대해 배운 것들을 사용해 학생, 버스, 지하철 세 가지의 객체가 서로 상호작용하는 것을 프로그래밍 한 예제다. package classpart; public class Student { String name; int money; public St

dogrushdev.tistory.com

 

OCP(Open Closed Principle)이란?

 

nesoy.github.io

 

090 OOP-예제로시작하기-01 객체와 클래스(Object & Class)

#파이썬 #python #객체_지향 #oop #object_oriented_programming #class #object #클래스 #오브젝트 #객체 ...

blog.naver.com

 

자바의 추상 클래스와 인터페이스

추상 클래스와 인터페이스의 차이 | 추상 클래스(Abstract class)와 인터페이스(Interface)의 차이 및 존재 이유를 설명해주세요. 클래스는 크게 일반 클래스와 추상 클래스로 나뉘는데 추상 클래스는

brunch.co.kr

 

 

 

중. Triangular Output

 

다음 예제와 같이 삼각형 모양으로 ‘*’을 출력하세요. (공백의 개수와 별의 개수를 정확하게 확인해주시길 바랍니다. 🙆🏻‍♂️)

 

3   *
 ***
*****
6

     *
    ***
   *****
  *******
 *********
***********

 

 

추측) 

일단 주어진 수많큼 줄이 생긴다.

모든 줄의 가운데 별은 인자 값으로 받는 int star 값 자리에서 출력된다. 

2중 for문을 사용해야 할거 같다. 일단 첫번째 for문은 행을 출력할 용도.

두번째 for문은 열을 따져 별을 출력해야 하는 데 n-i 와 n+i 사이면 *을 출력하면 될 듯 하다.

 

소스코드) 

public class Test1 {
    public void solution(int star){
        String answer = "";
        for(int i=0; i<star; i++){
            for(int j=0; j<star+star; j++){
                answer += (j>=star-i && j<=star+i) ? "*" : " ";
                if(j == star+(star-1)){
                    answer += "\n";
                }
            }
        }
        System.out.println(answer);
    }

    public static void main(String[] args) {
        Test1 method = new Test1();
        int star = 9;
        method.solution(star);
    }
}

 

 


상. 상하좌우 큰 수 찾기

 

 

5x5 2차원 배열이 주어질 때 어떤 원소가 상하좌우에 있는 원소보다 클 때 해당 위치에 * 을 표시하는 프로그램을 작성하세요. 경계선에 있는 수는 상하좌우 중 존재하는 원소만을 비교합니다.

 

[[3,4,1,4,9],[2,9,4,5,8],[9,0,8,2,1],[7,0,2,8,4],[2,7,2,1,4]] 3 4 1 4 *
2 * 4 5 8
* 0 * 2 1
7 0 2 * 4
2 * 2 1 4

 

추측) 

상하좌우를 어떻게 비교해야할까 

일단 for문을 돌려 행으로 배열을 열고 그안에 for문을 3개를 돌려야 할듯하다. 3개의 for문이 같은 돌면서 뒤의 인덱스 같은 위치, 앞의 인덱스 다른 숫자를 출력해 이를 비교하고 중앙 경우 전후의 값까지 출력해서 중심이 되는 값이랑 비교하는 작업을 진행

모두의 숫자보다 크다면(이건 판별 카운트를 넣어야 겠다) 중앙 숫자를 *으로 바꿔 배열에 넣어주고 아니면 숫자를 그대로 배열에 넣어 주면 될 듯하다. 

경계부분 행 경우에는 행의 인덱스 0과 인덱스 (길이-1) 때에 for문을 하나씩 패스할 수 있게 하면 될거 같고

경계부분 열 경우에는 열의 인덱스 0과 인덱스 (길이-1) 때에 좌우 중 하나만 출력하게 처리하면 될거 같다.

 

소스코드) 

public class Test2 {
    public void solution(int[][] arr) {
        String answer = "";
        int centerValue = 0;
        int ckNum = 0; // 체크할 대상 개수
        int ckCnt = 0; // 큰 숫자가 있는 경우 올라가는 카운트

        for(int i=0; i<arr.length; i++){

            for(int k=0; k<arr[i].length; k++){
                // 중앙값 대입
                centerValue = arr[i][k];

                //왼쪽 경계 아니라면 실행
                if(k != 0){
                    ckNum++;
                    if (centerValue > arr[i][k-1]) ckCnt++;
                }
                //오른쪽 경계 아니라면 실행
                if(k != arr[i].length-1 ){
                    ckNum++;
                    if (centerValue > arr[i][k+1]) ckCnt++;
                }
                // 상
                if(i != 0){ // 맨 위일 경우 윗줄 판별은 필요없다.
                    ckNum++;
                    if (centerValue > arr[i-1][k]) ckCnt++;
                }
                // 하
                if(i != arr.length-1) { // 맨 아래일 경우 아랫줄 판별은 필요없다.
                    ckNum++;
                    if (centerValue > arr[i+1][k]) ckCnt++;
                }
                // 비교
                if(ckCnt == ckNum){
                    answer += "*";
                    answer += (k == arr[i].length-1) ? "\n" : " ";
                } else {
                    answer += Integer.toString(centerValue);
                    answer += (k == arr[i].length-1) ? "\n" : " ";
                }

                // 초기화
                ckCnt=0;
                ckNum=0;
            }
        }
        System.out.println(answer);
    }

    public static void main(String[] args) {
        Test2 method = new Test2();
        int[][] arr1 = {{7,4,6,5,9}, {6,1,3,4,5}, {4,8,5,6,9}, {1,3,0,6,4}, {6,4,8,1,7}};

        method.solution(arr1);
    }
}

 

리뷰) 

+ 중앙 값을 먼저 가져와야 한다.

+ for문 안에 병렬 for문이 3개나 필요없다 한개로 끝난다. 

+ 검사하는 숫자 개수와 큰 수 일때의 경우의 개수 비교가 중요하다. 

+ 배열의 위치를 잘 인지하고 있어야 풀 수 있다. 

 

+ Recent posts