مزایای برنامهنویسی تابعی در جاوا ۸

کماکان بحث در مورد مزایا و معایب جاوا ۸ وجود دارد در این مطلب یکی از مزایای آن را در حل یک مشکل مشاهده میکنیم.
یک مشکل رایج در تبدیل زوجی از اشیا مشابه به یکدیگر وجود دارد (به عنوان مثال تبدیل کلاسهای domain به DTOها که بعدا به شکل JSON به front end فرستاده شود و بالعکس). در این شرایط به یک تبدیل one-shot نیاز داریم، تنها یکبار در عمر شئ. چیزی که نمیخواهیم اتفاق بیوفتد coupling بین دو شئ است: کلاسهای DTO نباید در مورد کلاسهای دامنه چیزی بدانند و بالعکس.
به این دلیل که تغییرات در شئ منبع نباید تاثیری در شئ مقصد داشته باشد، راه حل قدیمی ساختن یک کلاس Mapper یا Converter برای هر زوج است. به علاوه ابزارهایی وجود دارند که میتوانند فیلدهای کلاسهای مشابه را بر اساس اسم فیلدها به هم نگاشت کنند. اما در مورد ساختن مجموعهای کامل از اشیا منبع چی؟ چگونه مجموعهای از اشیا مقصد را بدون کد اضافی تولید کنیم؟
چیزی که نیازهای ما را پاسخ میدهد جاوا ۸ و سه ویژگی اصلی آن است:
- پیادهسازی توابع پیش فرض در واسطها
- جویبارها (streams)
- لامبدا (در اینجا به فرم یک ارجاع به تابع)
پیادهسازی متد پیشفرض چیزی است که ما را از کد اضافی نجات میدهد. جویبارها و لامبدا نیز کد زیبایی برای تبدیل مجموعههای ما میسازند. بیایید به سلسله مراتب نهایی کلاس و کد نگاه کنیم:
public interface GenericConverter { E createFrom(D dto); D createFrom(E entity); E updateEntity(E entity, D dto); default List createFromEntities(final Collection entities) { return entities.stream() .map(this::createFrom) .collect(Collectors.toList()); } default List createFromDtos(final Collection dtos) { return dtos.stream() .map(this::createFrom) .collect(Collectors.toList()); } }
متد پیشفرض که یک مجموعه از اشیا انتقال داده (D) را به موجودیتها(E) تبدیل میکند را پیادهسازی کردیم. ما دیگر نیازی به پیادهسازی این در در پیادهسازی concrete مبدل خود نخواهیم داشت.ساخت یک مبدل برای یک DTO یا domain class تنها به سادگی زیر است:
@Component public class AccountConverterImpl implements AccountConverter { @Override public Account createFrom(final AccountDto dto) { return updateEntity(new Account(), dto); } @Override public AccountDto createFrom(final Account entity) { AccountDto accountDto = new AccountDto(); accountDto.setAccountType(entity.getAccountType()); accountDto.setActive(entity.getActive()); accountDto.setEmail(entity.getUserId()); ClassUtils.setIfNotNull( entity::getPassword, accountDto::setPassword); return accountDto; } @Override public Account updateEntity(final Account entity, final AccountDto dto) { entity.setUserId(dto.getEmail()); entity.setActive(dto.getActive()); ClassUtils.setIfNotNull( dto::getAccountType, entity::setAccountType); return entity; } }
ویژگی دیگری که میتوانید آنجا ببینید تابع ClassUtils.setIfNotNull است. که تنها یک setter فراخوانی میکند، اگر که getter مقدار غیر Nullای در اختیارش قرار دهد.
public class ClassUtils { protected ClassUtils() { } public static void setIfNotNull(final Supplier getter, final Consumer setter) { T t = getter.get(); if (null != t) { setter.accept(t); } } }
به این ترتیب یک ساختار کامل مبدل را با امکانات جاوا ۸ فراهم آوردیم. اضافه کردن یک مبدل جدید برای یک زوج دیگر موجودیت-DTO (مثل کاربر، آدرس و …) تنها نیاز به ساخت یک کلاس جدید UserConverterImpl دارد که UserConverter خود را پیادهسازی کند، که باید به دنبال آن GenericConverter هم پیادهسازی کند. به این طریق کلاس جدید قابلیت تبدیل مجموعهای از اشیا را خواهد داشت و این مساله را مدیون پیادهسازی تابع پیشفرض در واسط GeneticConverter هستیم که یکی از قابلیتهای سودمند جاوا ۸ است.
منبع:
من دارم در مورد مزایای برنامه نویسی functional در جاوا 8 مطالعه می کنم. ولی هر سمپلی که می بینم کاملا بدون ویژگی های جاوا هشت هم قابل پیاده سازی هست. مثلا همین مثال با یکی کلاس abstract هم قابل پیاده سازی هست. من منظور از مزایا حل یک مشکل اساسی با قابلیت های اضافه شده به زبان هست. مثلا امکانات جدید را مقایسه کنید با اضافه شدن gemeric به جاوا 5.
می توانیم بگیم مزیت اصلی funtional تنها کوتاه شدن و خواناتر شدن کد ها هست؟