برنامه نویسی

جریان در جاوا: مسترینگ یا سوء استفاده؟

زیبا، مختصر و مدرن به نظر می رسید. اما وقتی شش ماه بعد دوباره کد را بررسی کردیم، هیچ کس متوجه نشد که چه کار می کند. تسلط بود؟ یا فقط زیرکی بیش از حد مهندسی شده؟

جاوا استریم ها به نشانه ای از توسعه مدرن جاوا تبدیل شده اند. آنها که در جاوا 8 معرفی شدند، یک رویکرد کاربردی برای کار با مجموعه ها ارائه می دهند که به توسعه دهندگان امکان می دهد کد مختصر و رسا بنویسند. اما مانند هر ابزار دیگری، اثربخشی آنها بستگی به نحوه و زمان استفاده از آنها دارد.

استریم چیست؟

استریم ها در معرفی شدند جاوا 8، و اگرچه جاوا در حال حاضر در نسخه های بالاتر از 21 است، اما این نسخه نقطه عطف را مشخص کرد به روشی که جاوا امروزه استفاده می شود. درون آن است API Stream که امروزه بیش از حد توسط برنامه نویسان جاوا استفاده می شود.

جریان ها یک روش کاربردی و اعلامی برای پردازش داده ها ارائه می دهد. آنها اجازه می دهند تا با استفاده از عملیاتی مانند نقشه، فیلتر، کاهش و غیره، مجموعه داده ها را به طور موثر دستکاری کنند. به عنوان مثال:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// whitout streams
List<Integer> evenNumbers = new ArrayList<>();
for (Integer number : numbers) {
    if (number % 2 == 0) {
        evenNumbers.add(number);
    }
}


// with streams
List<Integer> evenNumbers = numbers.stream()
                                   .filter(number -> number % 2 == 0)
                                   .collect(Collectors.toList());
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این رویکرد مدرن است زیرا:

  • کد دیگ بخار را کاهش می دهد.
  • برنامه نویسی کاربردی را ترویج می کند.
  • این اجازه می دهد تا عملیات موازی به راحتی با parallelStream().

اما در تجربه من، استفاده بیش از حد از استریم ها منجر به پیچیده شدن درک کد می شود. بنابراین سادگی همیشه به معنای شفافیت نیست، و مشکلات در اینجاست.

معضل ارشد و جوان: ظریف اما نامفهوم در مقابل ساده اما قوی

هنگام تلاش برای توضیح اهمیت استریم ها، بیشتر اوقات با تصاویری مانند این روبرو شده ام:

jrsr

و از خودم می پرسم… 🤔 چرا سطح ارشد از Stream برای حل این مشکل استفاده می کند؟

بیایید آن را تجزیه کنیم.

  • رویکرد “جوانان”: یک حلقه ساده، ساده و واضح. این کار می کند، خواندن آن آسان است، و هر توسعه دهنده – جوان یا ارشد – می تواند آن را بدون تلاش زیاد درک کند.
  • رویکرد ” ارشد “: استفاده از Streams برای رسیدن به همان نتیجه اما با کد فشرده تر و اعلانی تر. در نگاه اول، ممکن است پیچیده تر یا “مدرن” به نظر برسد.

اما نکته اینجاست: آیا استفاده از Streams در این سناریو واقعاً ارزش افزوده دارد؟ یا پیچیدگی غیر ضروری را فقط برای نمایش یک ابزار ایجاد می کند؟

بیایید با یک مثال بسیار دیوانه وار دیگر که دیدم و سرم درد گرفت 😵‍💫😵‍💫.

تصور کنید که در حال تماشای فیلم های برنامه نویسی هستید و ناگهان یک پسر ظاهر می شود و کد زیر را به شما ارائه می دهد.

public class CodeVerification {

  private static final String NUMBERS = "12345";
  private static final Integer LENGTH_CODE = 10;

  private final Random random;

  public CodeVerification(Random random) {
    this.random = random;
  }

  public String generateCode(){
    var builder = new StringBuilder();

    for (int i = 0; i < LENGTH_CODE; i++) {
      var index = random.nextInt(NUMBERS.length());
      builder.append(NUMBERS.charAt(index));
    }

    return builder.toString();
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون، این توسعه دهنده از شما می خواهد Refactor را generateCode() روش او راه حل زیر را ارائه می دهد:

public String generateCode(){
    return IntStream.range(0, LENGTH_CODE)
        .mapToObj(i -> random.nextInt(NUMBERS.length()))
        .map(NUMBERS::charAt)
        .collect(StringBuilder::new, StringBuilder::append,
            StringBuilder::append)
        .toString();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

الان میبینیش؟ 🧐🧐 درست متوجه منظور من شدی!!!!

هر دو کد دقیقاً یکسان کار می کنند، اما چی میشه؟؟ ما از یک کاملاً می رویم قابل درک، خواندنی و قابل نگهداری است کد به کدی که درک آن دشوار است و ارزش افزوده یا بهبود عملکرد کد را ندارد. همچنین، کد بازسازی نشده است، این موضوعی است که می توانیم در پست دیگری درباره آن صحبت کنیم.

بنابراین، با آن، من به این نتیجه می رسم که یک ارشد کسی نیست که نحوه استفاده از Stream ها را به طور کامل بلد باشد و آنها را در همه جا با توجیه “بازسازی” کد اعمال کند. ارشد بودن درک این نکته است که refactoring همیشه به معنای “مدرن” یا “عملکردی” کردن کد نیست، بلکه به معنای شفاف تر، کارآمدتر کردن و نگهداری آسان تر است..

یک سالمند می داند که چه زمانی از یک ساده استفاده کند for حلقه بزنید زیرا ساده ترین و مؤثرترین راه حل است، و چه زمانی باید Stream ها را انتخاب کنید که واقعاً وضوح یا مزایای عملکرد را اضافه می کنند. کلید درک است مشکل، نه ابزار. اگر راه‌حل سنتی ساده‌تر است و کد را خوانا نگه می‌دارد، نیازی به پیچیده‌تر کردن آن با Streams برای پیروی از یک روند نیست.

ارزش واقعی یک سالمند در توانایی آنها برای تصمیم گیری عملی، بر اساس زمینه پروژه و نیازهای تیم نهفته است، نه صرفاً نشان دادن مهارت ها با یک ابزار. یک سالمند این را گاهی اوقات می فهمد کمتر بیشتر است.

با مثال بالا آیا این به این معنی است که از Streams استفاده نکنیم؟ 😢😢😢 مطلقا خیر، در اینجا من به شما راهی برای استفاده از جریان ها برای حل همان مشکل، حفظ خوانایی کد، نشان می دهم، اما این بدان معنا نیست که بهینه تر است.

public String generateCode(){
    IntStream randomIndexes = random.ints(LENGTH_CODE, 0, NUMBERS.length());

    Stream<Character> characters = randomIndexes.mapToObj(NUMBERS::charAt);

    String code = characters.map(String::valueOf)
        .collect(Collectors.joining());

    return code 
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این قابل خواندن تر است، درست است؟ این صرفاً به این دلیل است که ما جریان را به مراحل جدا می کنیم، یکی از بهترین روش های Streams این است ❌ چندین مرحله را در یک مرحله به هم نچسبانید.

بهترین شیوه ها: نحوه استفاده از Streams بدون سوء استفاده

در اینجا نکاتی وجود دارد که من در طول تجربه خود در کار با استریم ها به دست آورده ام

  • خوانایی را بر خلاصه بودن اولویت دهید:اگر درک یک Stream در نگاه اول دشوار باشد، احتمالاً نیاز به بازسازی دارد.
  • اجتناب از تودرتوی بیش از حد عملیات: عملیات پیچیده را با نام های توصیفی به مراحل میانی تقسیم کنید
  • وقتی یک حلقه ساده کافی است از استریم ها استفاده نکنید: برای کارهای ساده ای مانند اصلاح یک عنصر یا انجام محاسبات اولیه، ممکن است جریان ها غیر ضروری باشند.
  • از جریان های موازی با احتیاط استفاده کنید: در حالی که جریان های موازی (parallelStream()) می تواند عملکرد را بهبود بخشد، استفاده نادرست ممکن است منجر به مشکلات همزمان یا کاهش عملکرد شود.
  • در صورت لزوم نظرات را اضافه کنید: اگر از یک عملیات پیچیده در یک جریان استفاده می کنید، هدف آن را مستند کنید تا دیگران بتوانند به سرعت آن را درک کنند.

نتیجه گیری

Stream ها یک ویژگی قدرتمند در جاوای مدرن هستند، اما ارزش واقعی آنها در دانستن زمان و نحوه استفاده موثر از آنها نهفته است. در استفاده یا عدم استفاده از Streams هیچ “سنتی” وجود ندارد – این فقط یک روش متفاوت برنامه نویسی است. یک توسعه‌دهنده ارشد می‌داند که موضوع ابزار نیست، بلکه انتخاب راه‌حل مناسب برای مشکل است. خواه این به معنای استفاده از Streams، حلقه ها یا هر رویکرد دیگری باشد، نکته کلیدی نوشتن کدی است که تمیز، قابل نگهداری و قابل درک باشد.

هدف همیشه باید کد تمیز، قابل نگهداری و قابل فهم باشد، به یاد داشته باشید: وضوح همیشه حرف اول را می زند.

جریان‌ها در صورت استفاده صحیح می‌توانند فوق‌العاده قدرتمند باشند، اما استفاده بیش از حد از آنها می‌تواند منجر به پیچیدگی غیر ضروری شود و به یاد داشته باشید: کمتر اغلب بیشتر است.

کد نویسی مبارک!! ❤️

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا