The Abstract Factory Pattern is a design pattern that provides an interface for creating families of related or dependent objets without specifying their concrete classes. This pattern is particularly useful when you need to create objects that are part of a group or have some common characteristics.

 

Key concepts of the Abstract Factory Pattern:

  1. Separate the process of creating objects from the actual implementation.
  2. Use interfaces to define factories and product families.
  3. Provide a common interface for creating a family of related objects.
  4. Enhance the maintainability and flexbility of the code by eliminating the need for hard-coded dependencies.

To further illustrate the Abstract Factory Pattern's advantages, let's consider an example involving a "Vehicle" class hierarchy. A vehicle can have different types such as car, motorcycle, or truck. Each vehicle type can have various attributes, such as color, engine type, number of wheels, etc. Using the Abstract Factory Pattern, you can create instances of different vehicle types while using the same construction process, making it easier to create different vehicle variations without having to write separate constructors for each combination.

 

Implementing the Abstract Factory Pattern:

  1. Create an interface for the abstract factory that declares methods for creating abstract products.
  2. Implement concrete factories that inherit from the abstract factory interface and create concrete products.
  3. Create an interface for each type of abstract product, which declares methods that the products must implement.
  4. Implement concrete products that inherit from the abstract product interfaces.
  5. Use the concrete factories to create instances of concrete products.

Here's a simple example of implementing the Abstract Factory Pattern using a "Vehicle" class hierarchy:

 

Abstract Vehicle class:

public abstract class Vehicle {
    public abstract String getColor();
    public abstract String getEngineType();
    public abstract int getNumberOfWheels();
}

 

Concrete Car class:

public class Car extends Vehicle {
    private String color;
    private String engineType;
    private int numberOfWheels;

    public Car(String color, String engineType, int numberOfWheels) {
        this.color = color;
        this.engineType = engineType;
        this.numberOfWheels = numberOfWheels;
    }

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public String getEngineType() {
        return engineType;
    }

    @Override
    public int getNumberOfWheels() {
        return numberOfWheels;
    }
}

 

Abstract Factory interface:

public interface VehicleFactory {
    Vehicle createVehicle(String color, String engineType, int numberOfWheels);
}

 

Concrete CarFactory class:

public class CarFactory implements VehicleFactory {
    @Override
    public Vehicle createVehicle(String color, String engineType, int numberOfWheels) {
        return new Car(color, engineType, numberOfWheels);
    }
}

 

Client code:

public class Main {
    public static void main(String[] args) {
        VehicleFactory carFactory = new CarFactory();
        Vehicle car = carFactory.createVehicle("Red", "Gasoline", 4);
        System.out.println("Car color: " + car.getColor());
        System.out.println("Car engine type: " + car.getEngineType());
        System.out.println("Car number of wheels: " + car.getNumberOfWheels());
    }
}

Using the Abstract Factory Pattern improves the maintainability and flexibility of your code, making it easier to create complex objects with various attributes, and simplifying the process of extending the code with new types of objects.

'Java > Design Pattern' 카테고리의 다른 글

Understand of Builder Pattern  (0) 2023.04.06


Recently,  while studying Spring Security, I noticed a repetitive pattern in instance creation. I discovered that Java employed well-known design patterns following Object-Oriented Programming (OOP) principles. Today, I will explore the Builder Pattern sugin some exmaples.

 

The Builder Pattern is a design pattern that separates the construction of a complex object from its representation, allowing the same construction process to create different representations. This pattern is particularly useful when an object has many optional attribute, and it reduces the need for numerous constructors or complex parameter sets.

 

An example of using the Builder Pattern is when you have a class representing a sandwich. A sandwich can have various combinations of ingredients like bread, cheese, vegetable, and sause, Using the Builder Pattern, you can create different sandwich instances with different ingredients while following the same construction process

 

Key concepts of the Builder Pattern:

 

  1. Separate the construction of a complex object from its representation.
  2. Use the same construction process to create different representations.
  3. Reduce the need for multiple constructors or complex parameter sets.
  4. Simplify the creation of complex objects, making the code more maintainable and flexible.

 

To further illustrate the Builder Pattern's advantages, let's consider an example involving a "Car" class. A car can have different attributes such as color, engine type, number of doors, etc. Using the Builder Pattern, you can create car instances with different attributes while using the same construction process, making it easier to create different car variations without having to write separate constructors for each combination.

Implementing the Builder Pattern:

 

  1. Create the Builder class as a Static Nested Class. By convention, append "Builder" to the target class name. For example, the Builder class for a Car class would be named "CarBuilder".
  2. Make the Builder class's constructor public and accept essential values as parameters.
  3. Provide methods for optional values, ensuring that each method returns the Builder object itself.
  4. Define the build() method within the Builder class to provide the final product to the client program. Since object creation is provided only through build(), the target class's constructor should be private.
  5.  
public class Car {
    //required parameters
    private String engine;
    private int tires;

    //optional paramters
    private boolean ambientColor;
    private boolean extraTire;

    public String getEngine() {
        return engine;
    }

    public int getTires() {
        return tires;
    }

    public boolean isAmbientColor() {
        return ambientColor;
    }

    public boolean isExtraTire() {
        return extraTire;
    }

    private Car(CarBuilder builder) {
        this.engine = builder.engine;
        this.tires = builder.tires;
        this.ambientColor = builder.ambientColor;
        this.extraTire = builder.extraTire;
    }
    
    //Builder Pattern
    public static class CarBuilder{
        //required parameters
        private String engine;
        private int tires;

        //optional paramters
        private boolean ambientColor;
        private boolean extraTire;

        public CarBuilder(String engine, int tires) {
            this.engine = engine;
            this.tires = tires;
        }

        public void setAmbientColor(boolean ambientColor) {
            this.ambientColor = ambientColor;
        }

        public void setExtraTire(boolean extraTire) {
            this.extraTire = extraTire;
        }

        public Car build() {
            return new Car(this);
        }
    }
}

 

Using the Builder Pattern improves the maintainability and flexibility of your code, making it easier to create complex objects with various attributes.

 

This example is a code sinppet from the SecurityFilterChain class, used to configure an HttpSecurity instance. Although the concept is more complex than the previous example, it still applies the Builder Pattern to create an HttpSecurity instances.

 

    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .headers().frameOptions().sameOrigin() 
                .and()
                .csrf().disable()               
                .formLogin()                      
                .loginPage("/auths/login-form") 
                .loginProcessingUrl("/process_login")   
                .failureUrl("/auths/login-form?error")  
                .and()
                .logout()                       
                .logoutUrl("/logout")           
                .logoutSuccessUrl("/")
                .and()
                .exceptionHandling().accessDeniedPage("/auths/access-denied")
                .and()
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers("/orders/**").hasRole("ADMIN")       
                        .antMatchers("/members/my-page").hasRole("USER")  
                        .antMatchers("⁄**").permitAll()
                );              

        return http.build();
    }

 

In this code snippet, the Builder Pattern is used to configure various security settings for an HttpSecurity instance, such as headers, CSRF protection, authentication, and authorization. The builder methods are chained together to create a concise and readable configuration. The build() method at the end is called to create the final HttpSecurity instance with the specified configuration. This example demonstrates how the Builder Pattern can improve the readability and maintainability of code, even in more complex scenarios.

'Java > Design Pattern' 카테고리의 다른 글

About the Abstract Factory pattern  (0) 2023.04.09

Fundamental Software Desing Principles for Quality Coding

SOLID is an acroynum for the five object-oriented design. These five principles help us understand the need for certain design pattern and software architectrue in general. Adopting these principles can also contribute to avoiding code smells, refactoring code, and Agile or Adaptive software development.

 

SOLID Principes stand for:

  • Single Responsibility Principle
  • Open-Close Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)

In this article, we will look for the principles individually to be a better developer.

 

1. Single Responsibility Principle

 

A class should have one and only one reason to change, meaning that a class should have only one job. SRP does not necessarily mean that your class should only have one method or property, but rather that the functionality should be related to a single responsibility (and have only one reason for changing).

With SRP, classes become smaller and cleaner, making them easier to maintain.

 

1.2 examples of responsibilities that may need to be separated:

  • Persistence
  • Validation
  • Notification
  • Error Handling
  • Logging
  • Class Selection / Instantiation
  • Formatting
  • Parsing
  • Mapping

2. Open-Close principle

OCP states that “software entities such as modules, classes, functions, etc. should be open for extension, but closed for modification.” So what this principle wants to say is: We should be able to add new functionality without touching the existing code for the class. This is because whenever we modify the existing code, we are taking the risk of creating potential bugs. So we should avoid touching the tested and reliable (mostly) production code if possible.

 

   2.1 How to apply OCP:

  • Add the new functionalities by creating new derived classes which should be inherited from the original base class.
  • Allow the client to access the original class with an abstract interface through compositional design patterns like Strategy.

3. Liskov Substitution Principle (LSP)

LSP states that subclasses should be substitutable for their base classes. When we can successfully replace the object/instance of a superclass with an object/instance of the subclass, without affecting the behavior of the base class instance, then we are following LSP.

 

4.Interface Segregation Principle (ISP)

The ISP states that many client-specific interfaces are better than one general-purpose interface. Clients should not be forced to implement a function they do no need. It means that rather than one fat interface, numerous little interfaces are preferred, based on groups of methods, with each interface serving one submodule

 

The mitigation implies splitting the interface into smaller, dedicated interfaces, and using multiple inheritances of the interfaces on a class (a multifunction printer) that provides additional functionality.

 

5. Dependency Inversion Principle (DIP)

Entities must depend on abstractions, not on concretions. The DIP states that the high-level module must not depend on the low-level module, but they should depend on abstractions.

 

When a class knows about the design and implementation of another class, it raises the risk that changes to one class will break the other class. So, we must keep these high-level and low-level modules/classes loosely coupled as much as possible.

 

Conclusion

In this article, I explained the very basic concept of SOLID principles, and I hope you look for another example  of principles and violation cases. This is short and simplifed information post for my own self-study :)

What is Optional class

Optional is a container object used to contain not-null objects. It has been introduced in Java 8 to deal with NullPointerException. Sometimes we write some code, we encounter many cases which null check happens. By using Optional Class, we can specify alternate values to return or alternate code to run. 

 

Class Declaration

Following is the declaration for java.util.Optional<T> class −

public final class Optional<T> extends Object

 

Some useful Method 

Modifier and Type Method and Description
   static <T> Optional <T>    empty()

  : Return an empty Optional Instance
   T    get()

  : If a value is present in this Optional, returns the value, otherwise throws NoSuchElem    entException.
   boolean   isPresent()

  : Return true if there is a value present, otherwise false.
   static <T> Optional <T>    of(T value)

  : Returns an Optional with the specified present non-null value.
   static <T> Optional <T>    ofNullable(T value)

  : Returns an Optional describing the specified value, if non-null, otherwise returns an e    mpty Optional.
   T    orElse(T other)

  : Return the value if present, otherwise return other.

 

Example of Method

        Optional<Integer> optional = Optional.empty();
        // print value
        System.out.println("Optional: " + optional);
        System.out.println("Has value: " + optional.isPresent());

        Integer value1 = null;
        Integer value2 = new Integer(10);

        //Optional.ofNullable - allows passed parameter to be null.
        Optional<Integer> a = Optional.ofNullable(value1);
        //Optional.of - throws NullPointerException if passed parameter is null
        Optional<Integer> b = Optional.of(value2);
        //Optional .get - bring the value of primitive type from wrapped optional value
        Integer c = b.get();

        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);

        System.out.println("a.orElse() = " + a.orElse(new Integer(0)));
        System.out.println("b.orElse() = " + b.orElse(null));

print result

 

재귀 (Recursion) 란?

재귀함수란 일정조건에서 자기 자신을 다시 호출하는 것을 구현한 함수이다. 주어진 문제를 해결하기 위해 원래 문제에서 작은 범위의 하위 문제를 먼저 해결함으로써 점점 더 큰 문제를 해결해나가는 방식입니다. 

 

이런 재귀함수는 자기자신을 계속 호출하기 때문에 무한루프에 빠질 가능성이 있다. 그래서 반드시 루프에 종료구간이 되는 Base Case를 생각해주어야 하고 그 Base Case에 도달하기 위한 Recursive Case를 잘 설정해주어야 한다.

 

재귀함수적 사고 해보기

  1. 재귀 함수의 입력값과 출력값 정의하기
    • 재귀 문제를 해결하기 위해선 내게 주어진 매개변수의 데이터타입과 그 갯수를 잘 확인해야한다.
    • 주어진 매개변수를 보통 가공한 다음에 Recursive Case의 매개변수로 다시 사용되기 때문에, 잘 확인할 필요가 있다.
  2. 문제를 쪼개고 경우의 수를 나누기
    • 1번단계에서 Recursive Case의 매개변수로 어떤것을 쓸지 정했다면, 그 매개변수가 어떤식으로 변해야 작은 문제를 해결가능한지 고민해보아야한다.
    • 문제에서는 요소의 앞쪽 인덱스부터 제거하라고 해도, 실제로는 뒤에서부터 제거했을 때 더 효율적인 코딩이 가능할 수 있다.
  3. 단순한 문제 해결하기
    • 이 단계가 Base Case이다. 재귀에서 더 이상 재귀할 수 없게 만드는 지점에 대해서 정의를 해주어야 한다.
    • 무한루프를 방지하고 최종연산 결과에 영향을 미치므로 그 값이 의도한 결과에 영향을 미치지않으면서 안정적으로 루프를 멈추어 내는 방향으로 고민을 해야한다.
  4. 복잡한 문제 해결하기
    • 문제를 잘개 쪼개는 것이다.
    • 실제로 재귀가 발생하는 지점으로 재귀를 어떤식으로 불러올지 고민해야한다.
    • 반복횟수를 통해서 루프를 통제할지 아니면 매개변수가 특정값에 도달하도록 루프를 통제할지 말이다.

 

말은 간단하지만 여러 문제를 접해보는게 가장 좋고

선 고민 후 왜 이런방식으로 접근을 했었는지 메타인지를 해보는게 중요하다

 

 

+ for문을 사용하면되는데 왜 재귀를 사용하는가?

1. 알고리즘 자체가 재귀적으로 표현하기 자연스러울 때.(가독성 상승)
2. 변수 사용을 줄여 준다.

3. 중첩된 반복문이 많거나, 그 중첩횟수를 정확히 알기 어려울 때.

 

1. 클래스

 : 클래스(Class)객체를 정의한 '설계도(blueprint)' 또는 '틀(frame)’이라 정의할 수 있습니다. 즉, 클래스는 객체를 생성하는데 사용되며, 반대로 객체는 클래스에 정의되고 설계된 내용 그대로 생성되는 한개의 예시라 볼 수 있죠. 즉 클래스는 객체 그 자체가 아니라 단지 객체를 생성하는 데 사용되는 하나의 틀으로 보야아 한다.

 

1.1]  클래스의 구성요소와 기본 문법

재벌집손자로 알아본 클래스 기본요소

  1. 필드 - 클래스의 속성을 나타내는 변수로, 클래스가 가지는 정보로 나타낼 수 있다. (현금재산, 주식자산, 나이)
  2. 메서드 - 클래스의 기능을 나타내는 함수로 각 객체가 하는 동작을 표현한다 (밥먹기, 잠자기, 회사경영 등등)
  3. 생성자 - 클래스의 객체를 생성하는 역할을 한다. 이에대한 내용은 다음포스트에서 다루도록하자.
  4. 이너 클래스 - 클래스 내부의 클래스를 의미하는데, 이 내용도 다음포스트로 미루겠슴..

여기서 간단히 요약하면 필드와 메서드는 각각의 클래스가 가지는 속성(state)와 기능(behavior)을 대표합니다. 속성과 기능은 해당 클래스와 관련된 데이터의 집합이며, 핵심적인 정보가 된다

 

 

 

 

재벌집손자 객체 만드는 방법

2. 객체

: '틀'이라는 클래스로 만들어진 각각의 존재들을 객체라 한다. 이런 객체들의 각각의 예는 인스턴스(Instance)라고 부르며, 이를 개념적으로 아우르는 것을 객체라 부른다 (Ex, 재벌집손자: 클래스, 손자1:진성준, 손자2: 진도준, 이라고 할때 각각진성준과 진도준은 인스턴스 이지만 재벌집손

자라는 클래스입장에서는 손자라는 객체로 묶어서 불러도 무방하다)

객체 생성의 구체적인 방향 by codestates

  • 클래스 = 재벌집손주, 클래스영역 = 정심재, 참조변수 = 진도준, 힙메모리 = 주민등록상 정보라고 생각을 해보자.
  • 재벌집손주 진도준 = new 재벌집손주();
  • 진성준이라는 '참조변수'는 진윤기라는 본인 부모님집의 '주소'를 가지고 있고 그건 주민등록상 집이다.
  • '힙메모리'라는 본가에는 본인에게 필요한 정보 와 동작이 저장되어 있다
  • 하지만 실제 도준이는 오피스텔 이라는 '스택영역'에 잠시 거주중이다.
  • '할아버지께 잘보여서 분당 땅 받기' , '할아버지께 대통령 당선자 미리 알려주기' 등과 같은 동작은 사실 클래스가 저장되어있는 정심재에서만 가능하다.

요악하면 : 진성준은 지금 오피스텔에 잠시 있지만, "너네 집 주소가 어디야?" 라고 물으면 부모님집을 말할것이고, 거기에 본인에게 필요한 정보와 동작이 저장되어 있다. 하지만 재벌집손주라는 클래스는 정심재에 영향을 받아 동작할것이다.

 

3. 필드

: 필드는 ‘클래스에 포함된 변수'를 의미하는 것으로 객체의 속성을 정의할 때 사용됩니다. 자바에서 변수는 크게

클래스 변수(cv, class variable), 인스턴스 변수(iv, instance variable), 그리고 지역 변수(lv, local variable)라는 세 가지로 구분될 수 있다. 이 중 우리가 필드라 부른 것은 클래스 변수와 인스턴스 변수이며, 이 둘은 다시 static 키워드의 유무로 구분할 수 있습니다. 좀 더 구체적으로, static 키워드가 함께 선언된 것은 클래스 변수, 그렇지 않은 것은 인스턴스 변수입니다. 그리고 이 두 가지 변수 유형에 포함되지 않고 메서드 내에 포함된 모든 변수를 지역변수라 부릅니다.

 

자! 예를 들어보자.

각각의 변수로 할아버지의 이름, 본인의 나이, 그리고 오늘 밤 잠잘 장소 이렇게 3가지가 있다고 해보자.

  • '할아버지의 이름' 진양철 은 클래스 변수로 내가 진성준이던 진도준인지 상관없이 모두 똑같다.
  • '본인의 나이'는 각각의 손주가 태어나던 해에 정해진다. 즉 각각의 손주가 다른값을 가지는 인스턴스변수다.
  • '오늘밤 잠 잘 장소'는 오피스텔에 살고있는 성준이가 하루하루 머무르는 지역변수 이다

 

1.1] 클래스 변수 VS 인스턴스 변수 VS 지역변수

클래스 변수란 한 클래스로부터 생성되는 모든 인스턴스 들이 특정한 값을 공유해야하는 경우에 주로 static 키워드를 사용하여 클래스 변수를 선언하게 된다.

 

    -> 모든 손주들이 공유하고 있는 진양철 할아버지에대한 정보는 다 똑같다.

 

: 클래스 변수는 인스턴스 변수와 달리 인스턴스를 따로 생성하지 않고도 언제라도 클래스명.클래스변수명 을 통해 사용이 가능합니다. 

 

    -> 할아버지는 손주가 태어나기전에 (인스턴스 생성전) 본인에 관한 정보를 다 가지며 이는 모든 손주들에게 똑같음

 

: 지역변수는 메서드 내에 선언되며 메서드 내({} 블록)에서만 사용가능한 변수입니다.

 

    -> 각각의 손주가 보내는 개인적인 시간은 정심재(클래스영역)과 주민등록상 정보(힙메모리)에 영향을 주지 않음

 

: 클래스변수와 인스턴스 변수는 필드(전역변수)로 묶일 수 있으며 지역변수와 다르게 초기값이 존재한다.

 

   -> 재벌집막내아들로 태어난 그 순간 정심재(=클래스 변수)와 주민등록상 정보(=인스턴스 변수) 에는 값이 존재하지만           하루하루 살아가며 생기는 정보와 동작은 아직 정해진것이 없다.

 

4. 메서드

메서드는 시그니처(method signature)와 몸통에 해당하는 메서드 바디(method body)로 구분이 됩니다. 메서드는

“특정 작업을 수행하는 일련의 명령문들의 집합"을 의미하며 클래스의 기능에 해당하는 내용들을 담당한다.

스태틱 메서드의 예시, 한개씩 보도록하자

  • public : 자바제어자로 다음 포스트에 기술하겠슴..
  • static : 클래스메서드가 되게 만드는 역할을 하며, 이는 클래스 변수와 똑같은 성질을 가진다. 모든 인스턴스들이 동일한 메서드를 공유하고, 그 메서드는 각 인스턴스들이 생성되기전에 이미 존재하고 있다.
    • Arrays.copyOfRange(); (예시)
    • System.arraycopy(); 
    • 인스턴스 메서드의 경우엔 생략 해야한다. 
  • int  : 메서드로 반활될 타입이다. 만약 반환할 값이 없다면 'void'를 써주면 되겠다.
  • 보너스 용돈받기 : 메서드의 이름
  • (int x, int y) : 메서드의 매개변수이다. 매개변수란 메서드 내부와 외부를 매개(연결)해주는 역할을 하는 변수이다.
    • 매개 변수로 받을 변수의 갯수와 타입을 정했다면, 인스턴스를 생성해서 그 인자를 입력받을땐  그에 맞게 받아주어야 한다.
  • 메서드 바디 : 메서드가 호출되었을 때, 실행되는 문(statement)이다

+메서드 오버로딩

: 메서드 중 1. 같은 이름을 가졌으나 2. 매개변수의 타입과 갯수가 다를 때 를 말한다. 이는 입력값은 다르지만 동일한 기능을 수행하는 메서드를 생성해야 할때, 이름낭비를 없도록 한다. 메서드 오버로딩에서 리턴값은 메서드 오버로딩과 상관없는 것이다.

1. 배열이란

  • 배열이란, 동일한 타입의 값들을 하나의 묶음으로 묶은 자료 구조를 의미한다.

배열의 기본형태

  • 배열은 타입을 먼저 선언하고 대괄호를 씀으로서, 해당변수가 배열임을 나타내고 그 값은 중괄호 " { } " 를 이용하여 묶어 표현한다.
  • 또 배열은 차원이라는 것이 있는데, 이는 배열이 중첩된 정도를 의미합니다. 즉, 배열이 중첩되었다 함은, 배열의 요소가 또 다른 배열인 경우를 의미한다.

1.1]  1차원 배열의 선언과 초기화

1차원 배열의 선언과 초기화

  1. 1번줄은 배열을 가리킬 codestates 참조변수를 선언한다.
  2. new double[140]은 double형 데이터를 가진 메모리을 확보한다.
  3.  codestates = new double[140] -> ' = ' 인 대입연산자를 이용하여 배열된 메모리중 첫번째의 주소값을 참조변수인 codestate에 할당한다

+ 참조변수를 사용하는 이유? 배열을 선언하는 시점에 배열이 몇개의 요소를 가질지 컴퓨터는 알 수 없다. 따라서 배열을 선언하면, 이후에 생성될 배열의 주소값을 담을 메모리 공간만이 확보된다. 이후 배열이 생성되고 난 다음에 해당 배열의 시작 주소값이 참조 변수에 할당되는 형태이다.

 

1.2] 2차원 배열의 선언과 초기화

이 안에 너 있다.

  1. 2차원 배열은 기본적으로 1차원 배열과 큰 차이가 없다. 다만 각 요소들을 묶어내는 방법이 다른것일 뿐이다 (해당예시는 140명의 사람을 한팀에 2명인 70팀은 만든것을 표현)
  2. 다만 2차원이상의 배열은 마지막 차수에 해당하는 배열의 길이를 고정하지 않아도 되는 가변배열을 만들 수 있다.
  3. 예시 double[1][2]에서 1번은 외부배열, 2번은 내부배열이라하는데, 여기서 외부배열인 1번은 그 크기를 '70'으로 정해야하지만 내부배열 '2' 은 없어도 상관이없다.

 

1.3] 배열의 탐색

for문을 사용한 배열의 요소 탐색

  • 배열은 기본적인 for문을 이용하여 요소들을 탐색할 수 있다.

while문을 사용한 배열의 요소탐색

  • 특별히 사용한적은 없다

enhanced for문을 이용한 배열의 요소탐색

  • Enhanced for문을 사용한 경우, 조금 더 직관적으로 볼수 있으나 2가지를 조심해야 한다.
  • for문 loop 안에서 index를 사용하지 않는 경우에 적합하다.
  • 배열의 값만을 불러오기 때문에, 값을 변경하기는 불가능하다.

1.4] 배열에서 많이 사용될것같은 메서드 모음(지극히 주관적)

Arrays.copyOf 메서드

  • 해당 메서드는 arr1과 같은 메서드를 elementNumber만큼의 요소를 가지고 만들어낸다. 만약 그 값이 기존의 요소의 최대갯수보다 큰 경우 그값은 0으로 초기화 된다.
  • Java에서는 파이썬과 달리 배열의 크기가 정해져있고 그 뒤에 직접적으로 붙일 수 있는 방법은 존재하지 않고, 새로운 배열을 생성해내고 마지막 요소공간에 값을 할당시키는 방법을 이용해야 한다?다만 이게 stable하거나 효율적인  방식인지는 난 몰라.. 그냥 이렇게 배움

 

 

Arrays.copyOfRange() 메서드

  • 전자와 다르게 index를 매개변수로 받고 싶을 때 사용한다. 갯수는 모르겠고 #1번째 index부터 #4번 index 전까지만 모여!
  • 새로운 배열을 만들어야 할때, 요소의 갯수는 count하기 번거로운 경우, 조금 더 간편하다.

 

System.arraycopy

  • 이 메서드는 arr1의 값들을 ctrl + c 해서 arr2 ctrl+v 한 것이다. 다만 arr1에선 #n1 인덱스부터 복사하고 arr2에선 #n2부터 붙여넣기 한다는 뜻에 elementNums갯수만큼만 한다는 이야기다.
  • 이 메서드는 사용하기 위해서 값을 붙여넣기 당하는 arr2가 미리 선언되어 있어야하고 그 크기도 정해져있어야 한다.
  • 각각의 복사점을 다르게 해서 특정부분만을 추출해야할 때 강력한 메서드가 될 수 있다.

1. for 문

for문 기본형태

  • for문은 조건식이 참인 동안 주어진 횟수만큼 실행문을 반복적으로 수행한다.
  • for문 내의 변수명은 임의로 정할 수 있다.다만 반복문에는 i라는 변수명을 많이 사용한다 i는 iterator(반복계수)를 의미.
  • for문 조건에 들어갈 변수가 외부에서 이미 초기화 되어있다면 생략가능하다.
  • 변수의 증감정도는 +=, -=, *=, /= 등의 복합연산자를 이용해 다양한 표현이 가능하다.
  • 반복문을 작성할땐, 조건에 따른 ( = ) 등호가 들어가야할지 말아야 할지를 판단하는 것이 중요하다.

1.1 향상된 for문

배열수업이후 추가 예정

 

2. while 문

while문 기본형태

  • while문 조건식에 사용될 변수는 외부에서 선언&할당 되어 있어야 한다.
  • 조건식 내부에 n++, n-- 와 같이 증감문을 넣어서 사용할 수 있다. (가독성이 좋은지는 모르겠음)
  • while문에 조금 더 좋은 경우로는, 조건식에 boolean 값이 들어가서 사용할 때, 조금 더 적절한것 같다.
  • 그렇다하더라도 for문과 서로 대체가능하기에 필요에 따라 적절히 이용하면 된다.

 

3. 소수(Prime Number)

  • 흔히 말하는 소수의 정의는 1과 자기자신을 제외한 숫자에서 그 수를 두 자연수의 곱으로 표현할 수 없는를 뜻한다.
  • 기본적인 소수 구하기는 이중 반복문을 이용하여 구할 수 있지만, 이엔 필요없는 연산이 많아서 그 과정을 줄일 수 있는 부분이 많다. 먼저 다음과 같다

소수 primeNumber 구하기

2번과 같이 사용할 수 있는 근거는?

  1. 먼저 '2'를 제외한 모든 짝수는 소수가 아니다. 왜냐하면 2로 나누어지기 때문이다.
  2. 1번을 근거로 반복문은 3부터 시작하여 증감정도를 2로하여 홀수만 판단할 수 있다.
  3. 어떤 자연수가 두 자연수의 곱으로 표현될 수 있다(소수가 아니라면), 두 자연수 중 작은값의 최대가 될 수 있는 값은 그 수의 거듭제곱근 까지이다.
  4. 3번의 예를 들면 81을 두 수의 곱으로 표현한다고 하면, (3 X 27) -> (5 X 16.2) -> (7 X11.57) ->(9 X9) 이렇게 구해 볼 수 있는데, 2번째와 3번째의 경우는 자연수가 아니므로 제외하면 81의 거듭제곱근인 9가 작은값중 최댓값이 될 수 밖에없다. 따라서 해당경우 까지만 반복문을 실행하면 된다.

 

++소수를 구하는 또다른 방식으로는 에라토스테네스의 체가 있다. 그치만 다음에 알아보도록하자. 어쩌면 배열 수업 이후?

+ Recent posts