dev.jaieve 공부기록

[Java] <클린코드> 속 객체지향의 특징 - 캡슐화와 다형성 본문

Back/Java

[Java] <클린코드> 속 객체지향의 특징 - 캡슐화와 다형성

제이브 2022. 2. 26. 22:15

인프런의 스프링 핵심원리 기본편 강의를 들으면서 객체지향의 특징에 대한 내용을 공부했다. 자바 공부를 시작한 뒤 잊을만하면 들려오는 핵심 개념이라서 이번 기회에 정리해볼까 한다.

이 포스팅은 현재 읽고 있는 로버트 마틴의 <클린코드>에 나오는 해당 부분만 골라서 블로그에 모은 글을 짧게 정리하는 글이다.

 


캡슐화

캡슐화란 객체의 실제 구현을 외부로부터 감추는 방식을 말한다. 세상의 모든 프로젝트는 오픈소스와 라이브러리를 사용하고 있다. 우리가 만든 코드에 외부에서 들어온 코드를 깔끔하게 통합시키기 위해서는 경계를 잘 지어야 한다.

 

a. 클린코드 8장 모호한경계를 구분짓기 - 우리 코드를 보호하기

<클린코드>에서는 특정 상황을 가정한다. 프로그램에 Senser객체(역할)가 있어 관리되고 있고, 이는 외부에서 사용되는 상황이다. SensorId와 Sensor 객체를 저장하기 위해서 Map을 사용할 수 있지만, Map을 사용하면 인터페이스가 제공하는 clear() 등 불필요한 기능이 노출된다. 만약 외부코드가 함부로 호출하게된다면 sensor 데이터가 손상될 수 있고, 이는 의도와 벗어난다.

 

 

Map<Sencor> sensors = new HashMap<Sensor>();
Sensor s = sensors.get(sensorId);

위의 코드를 캡슐화한다면 원하는 기능만 공개할 수 있고, 적절한 경계로 우리 코드를 보호할 수 있게 된다.

public class Sensors {
	private Map<Sensor> sensors = new HashMap<Sensor>();

	public Sensor getById(String sensorId) {
		return sensors.get(sensorId);
	}
}

다형성

b. 클린코드 8장 모호한경계를 구분짓기 - 외부코드와 호환하기

 

 

 

클린코드에서는 외부코드를 호출 할 때 우리가 정의한 인터페이스 대로 호출하기 위해 Adapter 패턴을 소개한다.

Adapter 패턴을 보니 핵심원리 기본편 강의 목록 중에서도 좋은 객체 지향 프로그래밍이란? 강의에서 중요하게 얘기하는 다형성(Polymorphism)이란 단어가 떠올랐다.

 

 

인프런 강의에서의 다형성의 장점

  • 프로그램을 사용하는 클라이언트는 대상의 역할만 알면된다.
  • 클라이언트는 구현 대상의 내부구조를 몰라도 된다.
  • 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
  • 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다.

Adapter 패턴이 다형성의 장점중에서도 “구현 대상 자체를 변경해도 영향을 받지 않는다”는 개념을 활용하여 만든 패턴이라 생각한다.

클린코드에서는 Elastic Search라는 오픈소스의 코드를 Adapter 패턴을 사용하는 예제코드로 보여준다.

 

1. Elastic Search 오픈소스에는 netty 라는 외부코드가 사용되고 있다.

package org.elasticsearch.http.nio;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Uppooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdpter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
import org.elasticsearch.ExceptionHelper;
import org.elasticsearch.nio.FlushOperations;
import org.elasticsearch.nio.Page;
import org.elasticsearch.nio.WirteOperation;

import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.function.BiConsumer;

class BettyAdapter{ ... }

2. 외부코드를 감싸기 위해 NettyAdaptor 를 사용했고,

class NettyAdptor {
	private final EmbeddedChannel nettyChannel;
	private final LinkedList<FlushOperation> flushOperations = new LinkedList,.();

	NettyAdaptor(ChannelHandler... handlers) {
		nettyChannel = new EmbeddedChannel();
		nettyChannel.pipeline().addLast("write_captor", new ChannelOutboundHandlerAdptor() {
			@Override
			public void write(ChannelHandlerContext ctx.Object msg, ChannelPromise promise) {
				// This is a little tricky. The embedded channel will complete the promise once it writes the message
				// to its outbound buffer. We do not want to complete the promise until the message is sent. So we
				// intercept the promise and apss a different promise back to the rest of the pipeline.
				try {
					ByteBuf message = (ByteBuf) msg;
					promise.addListener((f) -> message.release());
					NettyListener listener = NettyLKistener.fromChannelPromise(promise);
					flushOperations.add(new FlushOperations(message.nioBuffers(), listener));
				} catch (Exception e) {
					promise.setFailure(e);
				}
			}
		});
		mettyChannel.pipeline().addLast(handlers);
	}
	...
}
public int read(ByteBuffer[] buffers){
		ByteBuf byteBuf = Unpooled.wrappedBuffer(buffers);
		int initialREaderIndex = byteBuf.readerInder();
		nettyChannel.writebound(byteBuf);
		return byteBuf.readerIndex() = initialReaderIndex;
}

public int read(Page[] pages) {
		ByteBuf byteBuf = PagedByteBuf.byteBufFromPages(pages);
		int readableBytes = byteBuf.readableBytes();
		nettyChannel.writeInboud(byteBuf);
		return readableBytes;
}

3. HttpReadWriteHandler(클라이언트)에서 NettyAdapter 의 기능을 사용한다.

@Override
public int consumeReads(InboundChannelBuffer channelBuffer) {
	assert channelActive : "channelActive Should have been called";
	int bytesConsumed = adaptor.read(channelBuffer.sliceAndRetainPagesTo(channelBuffer.getIndex());
	Object message;
	while((message = adaptor.pollInboundMessage()) != null) {
		++inFlightRequests;
		requestSinceReadTimeoutTrigger = true;
		handleRequest(message);
	}
	return bytesConsumed;
}

 

강의를 통해 어떤 코드에서 다형성이 장점이 부각되는지 직접 코드를 따라서 작성하고, 이론적인 내용을 참고할 수 있었다. 강의 중에 <클린코드> 에 등장하는 SOLID 법칙에 대한 내용을 보고나서 클린코드를 읽고 정리했던 내용을 다시 읽어보니 1회독할 때는 너무 어려워서 와닿지 않았던 다형성과 캡슐화에 대한 그림이 머리속에 흐릿하게 그려지는 것 같았다.

물론 위에서 등장한 elastic search의 adapter 패턴은 완벽히 이해하지는 못했지만 다형성이란 개념에 대해서는 확실히 알게 됐다.

반응형

'Back > Java' 카테고리의 다른 글

[Java] JAR와 WAR의 차이  (0) 2021.07.18