자바 안드로이드 앱개발 강좌, 틀린 객체지향 프로그래밍

안드로이드 앱 개발에서 객체지향 프로그래밍(OOP)은 매우 중요한 개념입니다. 이 글에서는 객체지향 프로그래밍의 원칙과 자주 발생하는 실수, 그리고 이를 자바 코드를 통해 어떻게 해결할 수 있는지를 심도 깊게 다룰 것입니다. 이 강좌는 안드로이드 개발에 수월하게 접근할 수 있도록 도와주는 유용한 정보를 제공합니다. 각 섹션에서 예제 코드가 포함되어 있어 보다 쉽게 이해할 수 있습니다.

1. 객체지향 프로그래밍(OOP)란 무엇인가?

객체지향 프로그래밍은 소프트웨어 개발의 패러다임 중 하나로, 프로그램을 객체라는 독립적인 단위로 구성하여 코드를 모듈화하고 재사용성을 높이는 방식입니다. 자바는 객체지향 언어로, OOP의 기본 원칙인 캡슐화, 상속, 다형성을 지원합니다.

1.1 OOP의 기본 원칙

  • 캡슐화(Encapsulation): 객체의 속성과 메소드를 하나의 단위로 묶어 외부에서 접근할 수 없도록 보호하는 것입니다. 이를 통해 데이터의 무결성을 유지할 수 있습니다.
  • 상속(Inheritance): 이미 정의된 클래스를 기반으로 새로운 클래스를 정의하는 방법입니다. 코드의 재사용성을 높이고, 기능을 확장할 수 있습니다.
  • 다형성(Polymorphism): 동일한 메소드 이름이 다양한 형태로 동작할 수 있는 능력입니다. 이는 프로그램의 유연성을 높여줍니다.

2. 틀린 객체지향 프로그래밍의 위험성

OOP의 기본 원칙을 잘 지키지 않으면 코드의 가독성이 떨어지고, 유지보수가 어려워집니다. 다음은 OOP에서 자주 발생하는 실수들입니다:

2.1 불필요한 정보 노출

변수나 메소드가 공용(public)으로 설정되어 외부에서 자유롭게 접근할 수 있는 경우, 객체의 상태가 불안정해질 수 있습니다. 이러한 문제를 피하기 위해서는 변수에 접근하는 메소드를 설정하고, 직접적으로 접근하지 못하도록 해야 합니다.

예제 코드:

public class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0) {
            this.age = age;
        }
    }
}

2.2 비효율적인 상속 사용

상속을 남용하면 코드가 복잡해지고, 추적하기 어려운 버그가 발생할 수 있습니다. 필요한 경우에만 클래스를 상속하고, 가능한 한 ‘구성(Composition)’을 사용하는 것이 좋습니다.

예제 코드:

public class Car {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}

public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

2.3 다형성의 오용

다형성을 사용할 때는 메소드 오버로딩과 오버라이딩의 차이를 이해해야 합니다. 또한, 다형성 사용의 범위를 명확히 해야 하며, 자칫 잘못하다간 코드의 흐름이 복잡해질 수 있습니다.

예제 코드:

class Animal {
    void sound() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    void sound() {
        System.out.println("Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        myDog.sound();
        myCat.sound();
    }
}

3. 객체지향 설계 원칙(SOLID)

SOLID 원칙은 객체지향 설계를 더 잘 하기 위한 다섯 가지 원칙을 정리한 것입니다. 이 원칙을 기억하고 활용하면 더 나은 애플리케이션을 설계할 수 있습니다.

3.1 단일 책임 원칙(SRP)

한 클래스는 오직 하나의 책임만 가져야 하며, 그 책임이 완전히 캡슐화되어야 합니다. 이를 통해 클래스의 재사용성을 높이고, 변경이 용이하게 합니다.

3.2 개방-폐쇄 원칙(OCP)

소프트웨어 요소는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 합니다. 이를 통해 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있습니다.

3.3 리스코프 치환 원칙(LSP)

부모 클래스의 객체가 자식 클래스의 객체로 대체될 수 있어야 합니다. 이를 통해 시스템의 안정성을 유지할 수 있습니다.

3.4 인터페이스 분리 원칙(ISP)

클라이언트는 자신이 사용하지 않는 메소드에 의존하지 않아야 합니다. 이는 불필요한 기능을 강제로 구현하는 것을 방지합니다.

3.5 의존성 역전 원칙(DIP)

고수준 모듈은 저수준 모듈에 의존하면 안 되며, 두 모듈 모두 추상화에 의존해야 합니다. 이를 통해 모듈 간의 결합도를 낮출 수 있습니다.

4. 안드로이드 애플리케이션에서 OOP 적용하기

이제 안드로이드 애플리케이션 개발에서 OOP 원칙을 어떻게 적용할 수 있는지 살펴보겠습니다. Android Studio IDE를 사용하여 간단한 예제를 만들어 보겠습니다.

4.1 프로젝트 구성

안드로이드 스튜디오에서 새 프로젝트를 생성하고, 기본 템플릿을 선택합니다. 이후 다음과 같이 패키지 구조를 설정합니다:

  • com.example.myapp
  • model
  • view
  • controller

4.2 Model 정의하기

애플리케이션의 데이터를 표현하는 모델 클래스를 생성합니다. 예를 들어, 사용자(User) 모델을 정의할 수 있습니다.

예제 코드:

package model;

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

4.3 View 정의하기

사용자 인터페이스(UI)를 구성하는 뷰 클래스를 생성합니다. Android의 XML 레이아웃을 활용하여 UI를 정의합니다.

activity_main.xml 예제:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Welcome" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />
</LinearLayout>

4.4 Controller 정의하기

UI와 모델 간의 상호작용을 관리하는 컨트롤러 클래스를 정의합니다. MainActivity.java 파일에서 이 기능을 구현합니다.

예제 코드:

package controller;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import model.User;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Button button;
    private User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);
        button = findViewById(R.id.button);
        user = new User("John Doe");

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textView.setText("Hello, " + user.getName());
            }
        });
    }
}

5. 결론

객체지향 프로그래밍은 강력한 패러다임으로 안드로이드 애플리케이션 개발에 필수적입니다. 잘못된 OOP 원칙의 적용은 코드의 가독성과 유지 보수성을 저하시키지만, OOP 원칙을 올바르게 이해하고 적용한다면 효율적이고 안정적인 앱을 개발할 수 있습니다. 이 강좌에서는 OOP의 기본 개념과 자주 발생하는 실수를 다루었으며, 실용적인 예제를 통해 OOP의 적용 방법에 대해 알아보았습니다. 이제 여러분은 자바와 객체지향 프로그래밍의 원리를 통해 더 나은 안드로이드 애플리케이션을 개발할 수 있습니다.