Menjelajahi budaya Jawa - mengapa hal-hal begitu berat? Untuk apa itu dioptimalkan? [Tutup]


288

Saya sering kode dalam Python. Sekarang, untuk alasan pekerjaan, saya kode di Jawa. Proyek yang saya lakukan agak kecil, dan mungkin Python akan bekerja lebih baik, tetapi ada alasan non-teknik yang valid untuk menggunakan Java (saya tidak bisa masuk ke rincian).

Sintaks Java tidak ada masalah; itu hanyalah bahasa lain. Tetapi terlepas dari sintaksisnya, Jawa memiliki budaya, seperangkat metode pengembangan, dan praktik yang dianggap "benar". Dan untuk saat ini saya benar-benar gagal "grok" budaya itu. Jadi saya akan sangat menghargai penjelasan atau petunjuk ke arah yang benar.

Contoh lengkap minimal tersedia dalam pertanyaan Stack Overflow yang saya mulai: https://stackoverflow.com/questions/43619566/returning-a-result-with-several-values-the-java-way/43620339

Saya punya tugas - parse (dari satu string) dan menangani satu set tiga nilai. Dalam Python itu adalah one-liner (tuple), dalam Pascal atau C a 5-liner record / struct.

Menurut jawaban, setara dengan struct tersedia di sintaksis Java dan triple tersedia di perpustakaan Apache yang banyak digunakan - namun cara "benar" melakukannya sebenarnya dengan membuat kelas terpisah untuk nilai, lengkap dengan getter dan setter. Seseorang sangat baik untuk memberikan contoh yang lengkap. Itu adalah 47 baris kode (yah, beberapa baris ini kosong).

Saya mengerti bahwa komunitas pembangunan besar kemungkinan tidak "salah". Jadi ini masalah dengan pemahaman saya.

Praktik Python mengoptimalkan keterbacaan (yang, dalam filosofi itu, mengarah pada kemampuan pemeliharaan) dan setelah itu, kecepatan pengembangan. Praktik C mengoptimalkan penggunaan sumber daya. Untuk apa praktik Java dioptimalkan? Tebakan terbaik saya adalah skalabilitas (semuanya harus dalam keadaan siap untuk proyek jutaan-LOC), tetapi ini merupakan tebakan yang sangat lemah.


1
tidak akan menjawab aspek teknis dari pertanyaan tetapi masih dalam konteks: softwareengineering.stackexchange.com/a/63145/13899
Newtopian

74
Anda dapat menikmati esai Eksekusi
Kolonel Panic


3
Inilah yang menurut saya jawaban yang tepat. Anda tidak groking Java karena Anda berpikir tentang pemrograman seperti sysadmin, bukan programmer. Bagi Anda, perangkat lunak adalah sesuatu yang Anda gunakan untuk mencapai tugas tertentu dengan cara yang paling bijaksana. Saya telah menulis kode Java selama 20 tahun, dan beberapa proyek yang telah saya kerjakan membutuhkan waktu 20, 2 tahun untuk dicapai. Java bukan pengganti untuk Python atau sebaliknya. Mereka berdua melakukan pekerjaan mereka, tetapi pekerjaan mereka sama sekali berbeda.
Richard

1
Alasan bahwa Java adalah standar de-facto adalah karena itu memang benar. Itu diciptakan tepat, pertama kali, oleh orang-orang serius yang memiliki janggut sebelum janggut yang modis. Jika Anda terbiasa mengetuk skrip shell untuk memanipulasi file, Java tampaknya membengkak tak tertahankan. Tetapi jika Anda ingin membuat cluster server redundant ganda yang mampu melayani 50 juta orang sehari, didukung oleh caching redis cluster, 3 sistem penagihan, dan cluster database oracle 20 juta pound .. Anda tidak akan menggunakan python, bahkan jika mengakses database dalam Python adalah 25 baris lebih sedikit kode.
Richard

Jawaban:


237

Bahasa Jawa

Saya percaya semua jawaban ini tidak ada gunanya dengan mencoba menganggap maksud cara kerja Java. Verbositas Java tidak berasal dari itu menjadi berorientasi objek, seperti Python dan banyak bahasa lainnya juga belum memiliki sintaks terser. Verbositas Java juga tidak datang dari dukungan pengubah akses. Sebaliknya, ini hanyalah bagaimana Java dirancang dan telah berkembang.

Java awalnya dibuat sebagai C yang sedikit ditingkatkan dengan OO. Karena itu Java memiliki sintaks era 70-an. Selain itu, Java sangat konservatif dalam menambahkan fitur untuk mempertahankan kompatibilitas ke belakang dan untuk memungkinkannya bertahan dalam ujian waktu. Seandainya Java menambahkan fitur-fitur trendi seperti XML literals pada 2005 ketika XML adalah hal yang paling disukai, bahasa itu akan dibengkak dengan fitur hantu yang tidak dipedulikan siapa pun dan yang membatasi evolusinya 10 tahun kemudian. Oleh karena itu Java tidak memiliki banyak sintaksis modern untuk mengekspresikan konsep secara singkat.

Namun, tidak ada yang mendasar mencegah Java mengadopsi sintaksis itu. Misalnya, Java 8 menambahkan lambdas dan referensi metode, sangat mengurangi verbositas dalam banyak situasi. Java juga dapat menambahkan dukungan untuk deklarasi tipe data kompak seperti kelas kasus Scala. Tetapi Java tidak melakukannya. Perhatikan bahwa tipe nilai khusus ada di cakrawala dan fitur ini dapat memperkenalkan sintaks baru untuk mendeklarasikannya. Saya kira kita akan melihat.


Budaya Jawa

Sejarah perkembangan perusahaan Java telah membawa kita pada budaya yang kita lihat sekarang. Pada akhir 90-an / awal 00-an, Java menjadi bahasa yang sangat populer untuk aplikasi bisnis sisi server. Dulu aplikasi-aplikasi itu sebagian besar ditulis ad-hoc dan memasukkan banyak masalah kompleks, seperti HTTP API, basis data, dan pemrosesan umpan XML.

Pada tahun 00-an menjadi jelas bahwa banyak dari aplikasi ini memiliki banyak kesamaan dan kerangka kerja untuk mengelola masalah ini, seperti ORM Hibernate, parser XML Xerces, JSP dan servlet API, dan EJB, menjadi populer. Namun, sementara kerangka kerja ini mengurangi upaya untuk bekerja dalam domain tertentu yang mereka atur untuk diotomatisasi, mereka membutuhkan konfigurasi dan koordinasi. Pada saat itu, untuk alasan apa pun, sangat populer untuk menulis kerangka kerja untuk memenuhi kasus penggunaan yang paling kompleks dan karena itu perpustakaan ini rumit untuk diatur dan diintegrasikan. Dan seiring waktu mereka tumbuh semakin kompleks ketika mereka mengumpulkan fitur. Pengembangan perusahaan Java secara bertahap menjadi lebih dan lebih banyak tentang menyatukan perpustakaan pihak ketiga dan lebih sedikit tentang algoritma penulisan.

Akhirnya konfigurasi yang membosankan dan manajemen alat-alat perusahaan menjadi cukup menyakitkan sehingga kerangka kerja, terutama kerangka kerja Spring, datang untuk mengelola manajemen. Anda bisa meletakkan semua konfigurasi Anda di satu tempat, teorinya pergi, dan alat konfigurasi kemudian akan mengkonfigurasi potongan-potongan dan menyatukannya. Sayangnya "kerangka kerja" ini menambahkan lebih banyak abstraksi dan kompleksitas di atas seluruh bola lilin.

Selama beberapa tahun terakhir semakin banyak perpustakaan yang semakin ringan popularitasnya. Meskipun demikian, seluruh generasi programmer Java sudah cukup umur selama pertumbuhan kerangka kerja perusahaan yang berat. Teladan mereka, mereka yang mengembangkan kerangka kerja, menulis pabrik pabrik dan pemuat kacang konfigurasi proxy. Mereka harus mengkonfigurasi dan mengintegrasikan monstrositas ini sehari-hari. Dan sebagai hasilnya budaya masyarakat secara keseluruhan mengikuti contoh kerangka kerja ini dan cenderung terlalu merekayasa.


15
Yang lucu adalah bahwa bahasa lain tampaknya mengambil beberapa "java-isme". Fitur PHP OO sangat terinspirasi oleh Java, dan saat ini JavaScript sering dikritik karena sejumlah besar kerangka kerjanya dan betapa sulitnya untuk mengumpulkan semua dependensi dan memulai aplikasi baru.
marcus

14
@marcus mungkin karena beberapa orang akhirnya belajar "jangan menemukan kembali roda"? Ketergantungan adalah biaya untuk tidak menemukan kembali kemudi
Walfrat

9
"Terse" sering diterjemahkan menjadi one-liner yang misterius, "pintar" , kode-golf-ish.
Tulains Córdova

7
Bijaksana, jawaban yang ditulis dengan baik. Konteks sejarah. Relatif tidak terbuka. Bagus sekali.
Cheezmeister

10
@ TulainsCórdova Saya setuju bahwa kita harus "menghindari ... trik pintar seperti wabah" (Donald Knuth), tetapi itu tidak berarti menulis forperulangan ketika a mapakan lebih tepat (dan dapat dibaca). Ada media yang menyenangkan.
Brian McCutchon

73

Saya yakin saya punya jawaban untuk salah satu poin yang Anda ajukan yang belum diajukan oleh orang lain.

Java mengoptimalkan deteksi kesalahan programmer saat kompilasi dengan tidak pernah membuat asumsi

Secara umum, Java cenderung hanya menyimpulkan fakta tentang kode sumber setelah programmer secara eksplisit menyatakan niatnya. Kompiler Java tidak pernah membuat asumsi tentang kode dan hanya akan menggunakan inferensi untuk mengurangi kode yang berlebihan.

Alasan di balik filosofi ini adalah bahwa programmer hanya manusia. Apa yang kita tulis tidak selalu sesuai dengan tujuan program. Bahasa Jawa mencoba untuk mengurangi beberapa masalah tersebut dengan memaksa pengembang untuk selalu secara eksplisit menyatakan tipenya. Itu hanya cara mengecek bahwa kode yang ditulis benar-benar melakukan apa yang dimaksudkan.

Beberapa bahasa lain mendorong logika itu lebih jauh dengan memeriksa pra-kondisi, pasca-kondisi dan invarian (meskipun saya tidak yakin mereka melakukannya pada waktu kompilasi). Ini adalah cara yang bahkan lebih ekstrem bagi programmer untuk memeriksa kompilernya sendiri.

Dalam kasus Anda, ini berarti bahwa agar kompiler menjamin bahwa Anda benar-benar mengembalikan tipe yang Anda pikir Anda kembalikan, Anda perlu memberikan informasi itu kepada kompiler.

Di Jawa ada dua cara untuk melakukan itu:

  1. Gunakan Triplet<A, B, C>sebagai tipe kembali (yang benar-benar harus dalam java.util, dan saya tidak bisa menjelaskan mengapa hal ini tidak. Terutama karena JDK8 memperkenalkan Function, BiFunction, Consumer, BiConsumer, dll ... Sepertinya bahwa Pairdan Tripletsetidaknya akan masuk akal. Tapi saya ngelantur )

  2. Buat tipe nilai Anda sendiri untuk tujuan itu, di mana setiap bidang diberi nama dan diketik dengan benar.

Dalam kedua kasus tersebut, kompiler dapat menjamin bahwa fungsi Anda mengembalikan jenis yang dinyatakannya, dan bahwa pemanggil menyadari apa jenis setiap bidang yang dikembalikan dan menggunakannya sesuai.

Beberapa bahasa memang menyediakan pemeriksaan tipe statis dan inferensi tipe pada saat yang sama, tetapi hal itu membuat pintu terbuka untuk kelas halus masalah ketidakcocokan tipe. Di mana pengembang berniat mengembalikan nilai dari jenis tertentu, tetapi sebenarnya mengembalikan yang lain dan kompiler MASIH menerima kode karena kebetulan bahwa, secara tidak sengaja fungsi dan pemanggil hanya menggunakan metode yang dapat diterapkan pada kedua yang dimaksud dan tipe yang sebenarnya.

Pertimbangkan sesuatu seperti ini di dalam Skriptrip (atau tipe aliran) di mana inferensi tipe digunakan alih-alih mengetik secara eksplisit.

function parseDurationInMillis(csvLine){
    // here the developer intends to return a Number, 
    // but actually it's a String
    return csv.firstField();
}

// Compiler infers that parseDurationInMillis is String, so it does
// string concatenation and infers that plusTwoSeconds is String
// Developer actually intended Number
var plusTwoSeconds = 2000 + parseDurationInMillis(csvLine);

Ini adalah kasus sepele yang konyol, tentu saja, tetapi dapat menjadi jauh lebih halus dan menyebabkan masalah sulit untuk debug, karena kode terlihat benar. Masalah-masalah semacam ini sepenuhnya dihindari di Jawa, dan itulah sebabnya seluruh bahasa dirancang.


Perhatikan bahwa mengikuti prinsip Berorientasi Objek yang tepat dan pemodelan berbasis domain, kasus parsing durasi dalam pertanyaan terkait juga dapat dikembalikan sebagai java.time.Durationobjek, yang akan jauh lebih eksplisit daripada kedua kasus di atas.


38
Menyatakan bahwa Java mengoptimalkan kebenaran kode mungkin merupakan poin yang valid, tetapi bagi saya (pemrograman banyak bahasa, baru-baru ini sedikit java) bahwa bahasa gagal atau sangat tidak efisien dalam melakukannya. Memang, Java sudah tua dan banyak hal tidak ada saat itu, tetapi ada alternatif modern yang memberikan pemeriksaan kebenaran yang lebih baik, seperti Haskell (dan berani saya katakan? Karat atau Pergi). Semua kesulitan Jawa tidak diperlukan untuk tujuan ini. - Dan selain itu, ini tidak sama sekali menjelaskan budaya, tetapi Solomonoff mengungkapkan bahwa budaya adalah BS pula.
mulai

8
Sampel itu tidak menunjukkan bahwa inferensi jenis adalah masalah, hanya saja keputusan JavaScript untuk mengizinkan konversi tersirat dalam bahasa dinamis adalah bodoh. Bahasa dinamis yang waras atau bahasa yang dipuji secara statis dengan inferensi tipe tidak akan mengizinkan ini. Sebagai contoh untuk kedua jenis: python akan membuang runtime kecuali, Haskell tidak akan mengkompilasi.
Voo

39
@tomsmeding Perlu dicatat bahwa argumen ini sangat konyol karena Haskell telah ada selama 5 tahun lebih lama dari Jawa. Java mungkin memiliki sistem tipe, tetapi bahkan tidak memiliki verifikasi kebenaran saat dirilis.
Alexis King

21
“Java mengoptimalkan untuk deteksi kesalahan waktu kompilasi” - Tidak. Ini menyediakannya , tetapi, seperti yang telah dicatat orang lain, tentu saja itu tidak mengoptimalkannya dalam arti kata apa pun. Selain itu, argumen ini sama sekali tidak terkait dengan pertanyaan OP karena bahasa lain yang benar - benar mengoptimalkan untuk deteksi kesalahan waktu kompilasi memiliki sintaks yang jauh lebih ringan untuk skenario serupa. Verbositas over-the-top Java sama sekali tidak ada hubungannya dengan verifikasi waktu kompilasi.
Konrad Rudolph

19
@KonradRudolph Ya, jawaban ini benar-benar tidak masuk akal, meskipun saya kira itu menarik secara emosional. Saya tidak yakin mengapa ia memiliki begitu banyak suara. Haskell (atau mungkin lebih penting lagi, Idris) mengoptimalkan jauh lebih banyak untuk kebenaran daripada Java, dan memiliki tuple dengan sintaks yang ringan. Terlebih lagi, mendefinisikan tipe data adalah one-liner, dan Anda mendapatkan semua yang versi Java akan dapatkan. Jawaban ini adalah alasan untuk desain bahasa yang buruk dan verbositas yang tidak perlu, tetapi kedengarannya bagus jika Anda suka Java dan tidak terbiasa dengan bahasa lain.
Alexis King

50

Java dan Python adalah dua bahasa yang paling sering saya gunakan, tetapi saya datang dari arah lain. Yaitu, saya berada jauh di dunia Jawa sebelum saya mulai menggunakan Python jadi saya mungkin bisa membantu. Saya pikir jawaban untuk pertanyaan yang lebih besar dari "mengapa hal-hal begitu berat" datang ke 2 hal:

  • Biaya sekitar pengembangan antara keduanya seperti udara di balon hewan yang panjang. Anda dapat memeras satu bagian dari balon dan bagian lainnya membengkak. Python cenderung menekan bagian awal. Java meremas bagian selanjutnya.
  • Java masih kekurangan beberapa fitur yang akan menghilangkan sebagian dari bobot ini. Java 8 membuat penyimpangan besar dalam hal ini tetapi budaya belum sepenuhnya mencerna perubahan. Java dapat menggunakan beberapa hal lagi seperti yield.

Java 'mengoptimalkan' untuk perangkat lunak bernilai tinggi yang akan dipertahankan selama bertahun-tahun oleh tim besar orang. Saya sudah memiliki pengalaman menulis hal-hal dengan Python dan melihatnya setahun kemudian dan bingung dengan kode saya sendiri. Di Jawa saya bisa melihat potongan kecil kode orang lain dan langsung tahu apa fungsinya. Dengan Python Anda tidak bisa melakukan itu. Bukannya yang lebih baik seperti yang Anda sadari, mereka hanya memiliki biaya yang berbeda.

Dalam kasus spesifik yang Anda sebutkan, tidak ada tupel. Solusi mudahnya adalah membuat kelas dengan nilai publik. Ketika Jawa pertama kali keluar, orang melakukan ini dengan cukup teratur. Masalah pertama dengan melakukan itu adalah sakit kepala pemeliharaan. Jika Anda perlu menambahkan beberapa logika atau keamanan utas atau ingin menggunakan polimorfisme, Anda setidaknya harus menyentuh setiap kelas yang berinteraksi dengan objek 'tuple-esque' itu. Dalam Python ada solusi untuk ini seperti __getattr__dll sehingga tidak terlalu mengerikan.

Ada beberapa kebiasaan buruk (IMO) di sekitar ini. Dalam hal ini jika Anda menginginkan sebuah tuple, saya mempertanyakan mengapa Anda menjadikannya objek yang dapat diubah. Anda seharusnya hanya memerlukan getter (sebagai catatan, saya benci konvensi get / set tetapi ini adalah apa adanya). Saya pikir kelas telanjang (dapat diubah atau tidak) dapat berguna dalam konteks privat atau paket-privat di Jawa . Yaitu, dengan membatasi referensi dalam proyek ke kelas, Anda dapat refactor nanti sesuai kebutuhan tanpa mengubah antarmuka publik kelas. Berikut adalah contoh bagaimana Anda dapat membuat objek sederhana yang tidak dapat diubah:

public class Blah 
{
  public static Blah blah(long number, boolean isInSeconds, boolean lessThanOneMillis)
  {
    return new Blah(number, isInSeconds, lessThanOneMillis);
  }

  private final long number;
  private final boolean isInSeconds;
  private final boolean lessThanOneMillis;

  public Blah(long number, boolean isInSeconds, boolean lessThanOneMillis)
  {
    this.number = number;
    this.isInSeconds = isInSeconds;
    this.lessThanOneMillis = lessThanOneMillis;
  }

  public long getNumber()
  {
    return number;
  }

  public boolean isInSeconds()
  {
    return isInSeconds;
  }

  public boolean isLessThanOneMillis()
  {
    return lessThanOneMillis;
  }
}

Ini adalah semacam pola yang saya gunakan. Jika Anda tidak menggunakan IDE, Anda harus mulai. Ini akan menghasilkan getter (dan setter jika Anda membutuhkannya) untuk Anda sehingga ini tidak terlalu menyakitkan.

Saya akan merasa lalai jika saya tidak menunjukkan bahwa sudah ada jenis yang tampaknya memenuhi sebagian besar kebutuhan Anda di sini . Mengesampingkan itu, pendekatan yang Anda gunakan tidak cocok untuk Java karena bermain untuk kelemahan itu, bukan kekuatannya. Ini peningkatan sederhana:

public class Blah 
{
  public static Blah fromSeconds(long number)
  {
    return new Blah(number * 1000_000);
  }

  public static Blah fromMills(long number)
  {
    return new Blah(number * 1000);
  }

  public static Blah fromNanos(long number)
  {
    return new Blah(number);
  }

  private final long nanos;

  private Blah(long nanos)
  {
    this.nanos = nanos;
  }

  public long getNanos()
  {
    return nanos;
  }

  public long getMillis()
  {
    return getNanos() / 1000; // or round, whatever your logic is
  }

  public long getSeconds()
  {
    return getMillis() / 1000; // or round, whatever your logic is
  }

  /* I don't really know what this is about but I hope you get the idea */
  public boolean isLessThanOneMillis()
  {
    return getMillis() < 1;
  }
}

Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft

2
Bolehkah saya menyarankan Anda juga melihat Scala sehubungan dengan fitur Java "lebih modern" ...
MikeW

1
@ MikeW saya benar-benar mulai dengan Scala jalan kembali di rilis awal dan aktif di forum scala untuk sementara waktu. Saya pikir itu adalah pencapaian besar dalam desain bahasa, tetapi saya sampai pada kesimpulan bahwa sebenarnya bukan yang saya cari dan bahwa saya adalah pasak persegi di komunitas itu. Saya harus melihatnya lagi karena mungkin sudah berubah secara signifikan sejak saat itu.
JimmyJames

Jawaban ini tidak membahas aspek nilai perkiraan yang diajukan oleh OP.
ErikE

@ErikE Kata-kata "nilai perkiraan" tidak muncul dalam teks pertanyaan. Jika Anda dapat mengarahkan saya ke bagian spesifik dari pertanyaan yang tidak saya jawab, saya dapat berusaha melakukannya.
JimmyJames

41

Sebelum Anda terlalu marah di Jawa, silakan baca jawaban saya di posting Anda yang lain .

Salah satu keluhan Anda adalah perlunya membuat kelas hanya untuk mengembalikan sejumlah nilai sebagai jawaban. Ini adalah kekhawatiran yang valid yang menurut saya menunjukkan intuisi pemrograman Anda sudah benar! Namun, saya pikir jawaban lain tidak sesuai dengan tetap berpegang pada anti-pola obsesi primitif yang telah Anda komit. Dan Java tidak memiliki kemudahan yang sama untuk bekerja dengan beberapa primitif seperti yang dimiliki Python, di mana Anda dapat mengembalikan beberapa nilai secara asli dan menetapkannya ke beberapa variabel dengan mudah.

Tetapi begitu Anda mulai berpikir tentang apa yang dilakukan ApproximateDurationtipe untuk Anda, Anda menyadari bahwa itu tidak dibatasi secara sempit untuk "hanya kelas yang tampaknya tidak perlu untuk mengembalikan tiga nilai". Konsep yang diwakili oleh kelas ini sebenarnya adalah salah satu konsep bisnis domain inti Anda — kebutuhan untuk dapat merepresentasikan waktu dengan cara perkiraan, dan membandingkannya. Ini perlu menjadi bagian dari bahasa inti dari aplikasi Anda, dengan dukungan objek dan domain yang baik, sehingga dapat diuji, modular, dapat digunakan kembali, dan bermanfaat.

Apakah kode Anda yang menjumlahkan perkiraan durasi bersama-sama (atau durasi dengan margin kesalahan, namun Anda menyatakannya) sepenuhnya prosedural atau adakah objek-objek untuk itu? Saya akan mengusulkan bahwa desain yang baik sekitar penjumlahan durasi perkiraan bersama akan menentukan melakukannya di luar kode konsumsi, dalam kelas yang dengan sendirinya dapat diuji. Saya pikir menggunakan objek domain semacam ini akan memiliki efek riak positif dalam kode Anda yang membantu menjauhkan Anda dari langkah-langkah prosedural baris demi baris untuk menyelesaikan satu tugas tingkat tinggi (meskipun dengan banyak tanggung jawab), menuju kelas tanggung jawab tunggal yang bebas dari konflik keprihatinan yang berbeda.

Sebagai contoh, katakanlah Anda mempelajari lebih lanjut tentang presisi atau skala apa yang sebenarnya diperlukan untuk penjumlahan dan perbandingan durasi Anda agar berfungsi dengan benar, dan Anda mengetahui bahwa Anda memerlukan bendera perantara untuk menunjukkan "kesalahan sekitar 32 milidetik" (dekat dengan bujur sangkar) root 1000, jadi setengah logaritma antara 1 dan 1000). Jika Anda terikat pada kode yang menggunakan primitif untuk mewakili ini, Anda harus menemukan setiap tempat dalam kode di mana Anda memiliki is_in_seconds,is_under_1msdan mengubahnya menjadiis_in_seconds,is_about_32_ms,is_under_1ms. Semuanya harus berubah di semua tempat! Membuat kelas yang tanggung jawabnya adalah mencatat margin kesalahan sehingga dapat dikonsumsi di tempat lain membebaskan konsumen Anda dari mengetahui rincian margin kesalahan apa atau apa pun tentang bagaimana mereka digabungkan, dan memungkinkan mereka menentukan margin kesalahan yang relevan saat ini. (Yaitu, tidak ada kode konsumsi yang margin kesalahannya benar dipaksa untuk berubah ketika Anda menambahkan margin level kesalahan baru di kelas, karena semua margin kesalahan lama masih valid).

Pernyataan penutup

Keluhan tentang beratnya Java tampaknya kemudian hilang ketika Anda bergerak lebih dekat dengan prinsip-prinsip SOLID dan GRASP, dan rekayasa perangkat lunak yang lebih maju.

Tambahan

Saya akan menambahkan dengan sangat bebas dan tidak adil bahwa sifat otomatis C # dan kemampuan untuk menetapkan properti get-only dalam konstruktor membantu membersihkan lebih jauh lagi kode yang agak berantakan yang akan dibutuhkan "cara Java" (dengan bidang dukungan pribadi yang eksplisit dan fungsi pengambil / penyetel) :

// Warning: C# code!
public sealed class ApproximateDuration {
   public ApproximateDuration(int lowMilliseconds, int highMilliseconds) {
      LowMilliseconds = lowMilliseconds;
      HighMilliseconds = highMilliseconds;
   }
   public int LowMilliseconds { get; }
   public int HighMilliseconds { get; }
}

Berikut ini adalah implementasi Java dari hal di atas:

public final class ApproximateDuration {
  private final int lowMilliseconds;
  private final int highMilliseconds;

  public ApproximateDuration(int lowMilliseconds, int highMilliseconds) {
    this.lowMilliseconds = lowMilliseconds;
    this.highMilliseconds = highMilliseconds;
  }

  public int getLowMilliseconds() {
    return lowMilliseconds;
  }

  public int getHighMilliseconds() {
    return highMilliseconds;
  }
}

Nah, itu sangat sangat bersih. Perhatikan penggunaan immutabilitas yang sangat penting dan disengaja - ini tampaknya penting untuk jenis kelas penahan nilai ini.

Dalam hal ini, kelas ini juga merupakan kandidat yang layak untuk menjadi structtipe nilai. Beberapa pengujian akan menunjukkan apakah beralih ke struct memiliki manfaat kinerja run-time (bisa).


9
Saya pikir ini adalah jawaban favorit saya. Saya menemukan bahwa ketika saya mempromosikan kelas pemegang tempat pembuangan yang jelek seperti ini menjadi sesuatu yang dewasa, itu menjadi rumah bagi semua tanggung jawab terkait, dan semangat! semuanya menjadi lebih bersih! Dan saya biasanya belajar sesuatu yang menarik tentang ruang masalah pada saat yang bersamaan. Jelas ada keterampilan dalam menghindari over-engineering ... tapi Gosling tidak bodoh ketika dia meninggalkan sintaks tuple, sama dengan GOTO. Pernyataan penutupan Anda sangat bagus. Terima kasih!
SusanW

2
Jika Anda menyukai jawaban ini, bacalah tentang Desain Berbasis Domain. Ada banyak hal untuk dikatakan tentang topik ini yang mewakili konsep domain dalam kode.
neontapir

@neontapir Yap, terima kasih! Saya tahu ada nama untuk itu! :-) Meskipun saya harus mengatakan bahwa saya lebih suka ketika konsep domain muncul secara organik dari sedikit inspirasi refactoring (seperti ini!) ... ini seperti ketika Anda memperbaiki masalah gravitasi abad ke-19 dengan menemukan Neptunus .. .
SusanW

@ Susan, saya setuju. Refactoring untuk menghilangkan obsesi primitif dapat menjadi cara terbaik untuk mengungkap konsep domain!
neontapir

Saya menambahkan contoh versi Java seperti yang dibahas. Saya tidak mengerti semua gula sintaksis ajaib di C # jadi jika ada sesuatu yang hilang, beri tahu saya.
JimmyJames

24

Baik Python dan Java dioptimalkan untuk perawatan sesuai dengan filosofi desainer mereka, tetapi mereka memiliki ide yang sangat berbeda tentang cara mencapai ini.

Python adalah bahasa multi-paradigma yang mengoptimalkan kejelasan dan kesederhanaan kode (mudah dibaca dan ditulis).

Java (secara tradisional) adalah bahasa OO berbasis paradigma kelas tunggal yang mengoptimalkan untuk kejelasan dan konsistensi - bahkan dengan mengorbankan kode yang lebih bertele-tele.

Tuple Python adalah struktur data dengan sejumlah bidang tetap. Fungsionalitas yang sama dapat dicapai oleh kelas reguler dengan bidang yang dinyatakan secara eksplisit. Dalam Python adalah wajar untuk memberikan tupel sebagai alternatif untuk kelas karena memungkinkan Anda untuk menyederhanakan kode, terutama karena dukungan sintaksis bawaan untuk tupel.

Tetapi ini tidak benar-benar cocok dengan budaya Java untuk menyediakan pintasan seperti itu, karena Anda sudah dapat menggunakan kelas yang dinyatakan secara eksplisit. Tidak perlu memperkenalkan jenis struktur data yang berbeda hanya untuk menyimpan beberapa baris kode dan menghindari beberapa deklarasi.

Java lebih menyukai konsep tunggal (kelas) yang diterapkan secara konsisten dengan minimum gula sintaksis kasus khusus, sementara Python menyediakan beberapa alat dan banyak gula sintaksis untuk memungkinkan Anda memilih yang paling nyaman untuk tujuan tertentu.


+1, tetapi bagaimana kaitan ini dengan bahasa yang diketik secara statis dengan kelas, seperti Scala, yang tetap menemukan kebutuhan untuk memperkenalkan tupel? Saya pikir tuple hanyalah solusi yang lebih rapi dalam beberapa kasus, bahkan untuk bahasa dengan kelas.
Andres F.

@AndresF .: Apa yang Anda maksud dengan mesh? Scala adalah bahasa yang berbeda dari Jawa dan memiliki prinsip dan idiom desain yang berbeda.
JacquesB

Saya maksudkan itu sebagai balasan untuk paragraf ke-5 Anda, yang dimulai dengan "tetapi ini tidak benar-benar cocok dengan budaya Jawa [...]". Memang, saya setuju verbositas adalah bagian dari budaya Jawa, tetapi kurangnya tupel tidak bisa karena mereka "sudah menggunakan kelas yang dinyatakan secara eksplisit" - lagipula, Scala juga memiliki kelas eksplisit (dan memperkenalkan kelas kasus yang lebih ringkas ), tetapi juga memungkinkan tupel! Pada akhirnya, saya pikir kemungkinan Java tidak memperkenalkan tuple tidak hanya karena kelas dapat mencapai hal yang sama (dengan lebih banyak kekacauan), tetapi juga karena sintaksisnya harus tidak asing bagi programmer C, dan C tidak memiliki tupel.
Andres F.

@AndresF .: Scala tidak memiliki keengganan terhadap berbagai cara dalam melakukan sesuatu, ini menggabungkan fitur dari kedua paradigma fungsional dan dari OO klasik. Itu lebih dekat dengan filsafat Python dengan cara itu.
JacquesB

@AndresF .: Ya Java dimulai dengan sintaks dekat dengan C / C ++, tetapi banyak disederhanakan. C # dimulai sebagai salinan Jawa yang hampir persis, tetapi telah menambahkan banyak fitur selama bertahun-tahun termasuk tupel. Jadi itu benar-benar perbedaan filosofi desain bahasa. (Versi selanjutnya dari Java tampaknya kurang dogmatis. Fungsi orde tinggi pada awalnya ditolak karena Anda dapat melakukan kelas yang sama, tetapi sekarang telah diperkenalkan. Jadi, mungkin kita melihat perubahan dalam filsafat.)
JacquesB

16

Jangan mencari praktik; itu biasanya ide yang buruk, seperti yang dikatakan dalam Best practices BAD, pola GOOD? . Saya tahu Anda tidak meminta praktik terbaik, tetapi saya masih berpikir Anda akan menemukan beberapa elemen yang relevan di sana.

Mencari solusi untuk masalah Anda lebih baik daripada latihan, dan masalah Anda bukanlah tuple untuk mengembalikan tiga nilai di Java dengan cepat:

  • Ada Array
  • Anda dapat mengembalikan array sebagai daftar dalam satu-baris: Arrays.asList (...)
  • Jika Anda ingin menjaga sisi objek dengan pelat kurang mungkin (dan tanpa lombok):

class MyTuple {
    public final long value_in_milliseconds;
    public final boolean is_in_seconds;
    public final boolean is_under_1ms;
    public MyTuple(long value_in_milliseconds,....){
        ...
    }
 }

Di sini Anda memiliki objek tidak berubah yang hanya berisi data Anda, dan publik sehingga tidak perlu untuk getter. Perhatikan bahwa jika Anda menggunakan beberapa alat serialisasi atau layer persistence seperti ORM, mereka biasanya menggunakan pengambil / penyetel (dan dapat menerima parameter untuk menggunakan bidang alih-alih pengambil / penyetel). Dan inilah mengapa praktik ini banyak digunakan. Jadi jika Anda ingin tahu tentang praktik, lebih baik untuk memahami mengapa mereka ada di sini untuk penggunaan yang lebih baik.

Akhirnya: Saya menggunakan getter karena saya menggunakan banyak alat serialisasi, tetapi saya juga tidak menulisnya; Saya menggunakan lombok: Saya menggunakan pintasan yang disediakan oleh IDE saya.


9
Masih ada nilai dalam memahami idiom umum yang Anda temui dalam berbagai bahasa, karena mereka menjadi standar de facto yang lebih mudah diakses. Idiom-idiom itu cenderung berada di bawah judul "praktik terbaik" untuk alasan apa pun.
Berin Loritsch

3
Hei, solusi masuk akal untuk masalah ini. Tampaknya cukup masuk akal untuk hanya menulis kelas (/ struct) dengan beberapa anggota publik alih-alih solusi OOP over-engineered penuh dengan huruf [gs].
tommeding

3
Ini memang menyebabkan mengasapi karena, tidak seperti Python, pencarian atau solusi yang lebih pendek dan lebih elegan tidak dianjurkan. Tetapi kelebihannya adalah bahwa mengasapi akan lebih atau kurang sama untuk pengembang Java mana pun. Oleh karena itu ada tingkat pemeliharaan yang lebih tinggi karena pengembang lebih dapat dipertukarkan, dan jika dua proyek bergabung atau dua tim tidak sengaja menyimpang, ada sedikit perbedaan gaya untuk diperjuangkan.
Mikhail Ramendik

2
@MikhailRamendik Ini adalah trade-off antara YAGNI dan OOP. Orthodox OOP mengatakan bahwa setiap objek harus diganti; satu-satunya hal yang penting adalah antarmuka publik objek. Ini memungkinkan Anda untuk menulis kode yang kurang fokus pada objek konkret, dan lebih banyak pada antarmuka; dan karena bidang tidak dapat menjadi bagian dari antarmuka, Anda tidak pernah memaparkannya. Ini dapat membuat kode lebih mudah untuk dipertahankan dalam jangka panjang. Selain itu, memungkinkan Anda untuk mencegah negara tidak valid. Dalam sampel asli Anda, dimungkinkan untuk memiliki objek yang keduanya "<ms" dan "s", yang saling eksklusif. Itu buruk.
Luaan

2
@PeterMortensen Ini adalah perpustakaan Java yang melakukan banyak hal yang tidak berhubungan. Ini sangat bagus. Berikut adalah fitur
Michael

11

Tentang idiom Jawa secara umum:

Ada berbagai alasan mengapa Java memiliki kelas untuk semuanya. Sejauh pengetahuan saya, alasan utamanya adalah:

Java seharusnya mudah dipelajari untuk pemula. Semakin eksplisit, semakin sulit untuk melewatkan detail penting. Lebih sedikit keajaiban terjadi yang akan sulit untuk dipahami oleh pemula.


Adapun contoh spesifik Anda: garis argumen untuk kelas yang terpisah adalah ini: jika ketiga hal tersebut cukup kuat terkait satu sama lain sehingga mereka dikembalikan sebagai satu nilai, nilainya menyebutkan "benda" itu. Dan memperkenalkan nama untuk sekelompok hal yang terstruktur dengan cara yang sama berarti mendefinisikan kelas.

Anda dapat mengurangi pelat dengan alat-alat seperti Lombok:

@Value
class MyTuple {
    long value_in_milliseconds;
    boolean is_in_seconds;
    boolean is_under_1ms;
}


Ini juga mengapa program Java relatif mudah dipelihara!
Thorbjørn Ravn Andersen

7

Ada banyak hal yang dapat dikatakan tentang budaya Jawa, tetapi saya pikir dalam kasus yang Anda hadapi sekarang, ada beberapa aspek penting:

  1. Kode perpustakaan ditulis sekali tetapi digunakan lebih sering. Meskipun bagus untuk meminimalkan biaya penulisan perpustakaan, mungkin dalam jangka panjang akan lebih bermanfaat untuk menulis dengan cara yang meminimalkan biaya tambahan untuk menggunakan perpustakaan.
  2. Itu berarti tipe self-documenting hebat: nama metode membantu memperjelas apa yang terjadi dan apa yang Anda dapatkan dari suatu objek.
  3. Mengetik statis adalah alat yang sangat berguna untuk menghilangkan kelas kesalahan tertentu. Ini tentu saja tidak memperbaiki semuanya (orang-orang suka bercanda tentang Haskell bahwa begitu Anda mendapatkan sistem tipe untuk menerima kode Anda, itu mungkin benar), tetapi membuatnya sangat mudah untuk membuat beberapa jenis kesalahan yang salah menjadi mustahil.
  4. Menulis kode perpustakaan adalah tentang menentukan kontrak. Menentukan antarmuka untuk argumen dan jenis hasil Anda membuat batas-batas kontrak Anda lebih jelas. Jika sesuatu menerima atau menghasilkan tuple, tidak ada yang mengatakan apakah itu jenis tuple yang harus Anda terima atau hasilkan, dan ada sangat sedikit kendala dalam hal jenis generik (apakah ia memiliki jumlah elemen yang tepat? mereka dari tipe yang Anda harapkan?).

Kelas "Struct" dengan bidang

Seperti jawaban lain yang disebutkan, Anda bisa menggunakan kelas dengan bidang publik. Jika Anda membuat ini final, maka Anda mendapatkan kelas abadi, dan Anda akan menginisialisasi mereka dengan konstruktor:

   class ParseResult0 {
      public final long millis;
      public final boolean isSeconds;
      public final boolean isLessThanOneMilli;

      public ParseResult0(long millis, boolean isSeconds, boolean isLessThanOneMilli) {
         this.millis = millis;
         this.isSeconds = isSeconds;
         this.isLessThanOneMilli = isLessThanOneMilli;
      }
   }

Tentu saja, ini berarti Anda terikat pada kelas tertentu, dan apa pun yang pernah perlu untuk menghasilkan atau mengonsumsi hasil parse harus menggunakan kelas ini. Untuk beberapa aplikasi, itu tidak masalah. Bagi yang lain, itu bisa menyebabkan rasa sakit. Banyak kode Java tentang mendefinisikan kontrak, dan itu biasanya akan membawa Anda ke antarmuka.

Perangkap lain adalah bahwa dengan pendekatan berbasis kelas, Anda mengekspos bidang dan semua bidang itu harus memiliki nilai. Misalnya, isSeconds dan millis selalu harus memiliki beberapa nilai, bahkan jika isLessThanOneMilli benar. Apa yang seharusnya penafsiran nilai bidang milis ketika isLessThanOneMilli benar?

"Struktur" sebagai Antarmuka

Dengan metode statis yang diizinkan dalam antarmuka, sebenarnya relatif mudah untuk membuat tipe yang tidak dapat diubah tanpa banyak overhead sintaksis. Misalnya, saya mungkin menerapkan jenis struktur hasil yang Anda bicarakan sebagai sesuatu seperti ini:

   interface ParseResult {
      long getMillis();

      boolean isSeconds();

      boolean isLessThanOneMilli();

      static ParseResult from(long millis, boolean isSeconds, boolean isLessThanOneMill) {
         return new ParseResult() {
            @Override
            public boolean isSeconds() {
               return isSeconds;
            }

            @Override
            public boolean isLessThanOneMilli() {
               return isLessThanOneMill;
            }

            @Override
            public long getMillis() {
               return millis;
            }
         };
      }
   }

Itu masih banyak boilerplate, saya benar-benar setuju, tetapi ada beberapa manfaat juga, dan saya pikir itu mulai menjawab beberapa pertanyaan utama Anda.

Dengan struktur seperti hasil parse ini, kontrak parser Anda didefinisikan dengan sangat jelas. Dalam Python, satu tuple tidak benar-benar berbeda dari tuple lain. Di Java, pengetikan statis tersedia, jadi kami sudah mengesampingkan kelas kesalahan tertentu. Misalnya, jika Anda mengembalikan tupel dengan Python, dan Anda ingin mengembalikan tupel (milid, isSeconds, isLessThanOneMilli), Anda dapat melakukan:

return (true, 500, false)

ketika Anda maksud:

return (500, true, false)

Dengan antarmuka Java semacam ini, Anda tidak dapat mengompilasi:

return ParseResult.from(true, 500, false);

sama sekali. Kamu harus melakukan:

return ParseResult.from(500, true, false);

Itulah manfaat dari bahasa yang diketik secara statis pada umumnya.

Pendekatan ini juga mulai memberi Anda kemampuan untuk membatasi nilai apa yang bisa Anda dapatkan. Misalnya, ketika memanggil getMillis (), Anda dapat memeriksa apakah isLessThanOneMilli () benar, dan jika ya, lempar IllegalStateException (misalnya), karena tidak ada nilai milis yang berarti dalam kasus itu.

Menyulitkan Melakukan Hal yang Salah

Dalam contoh antarmuka di atas, Anda masih memiliki masalah yang Anda dapat menukar argumen isSeconds dan isLessThanOneMilli secara tidak sengaja, karena keduanya memiliki tipe yang sama.

Dalam praktiknya, Anda mungkin ingin memanfaatkan TimeUnit dan durasi, sehingga Anda akan mendapatkan hasil seperti:

   interface Duration {
      TimeUnit getTimeUnit();

      long getDuration();

      static Duration from(TimeUnit unit, long duration) {
         return new Duration() {
            @Override
            public TimeUnit getTimeUnit() {
               return unit;
            }

            @Override
            public long getDuration() {
               return duration;
            }
         };
      }
   }

   interface ParseResult2 {

      boolean isLessThanOneMilli();

      Duration getDuration();

      static ParseResult2 from(TimeUnit unit, long duration) {
         Duration d = Duration.from(unit, duration);
         return new ParseResult2() {
            @Override
            public boolean isLessThanOneMilli() {
               return false;
            }

            @Override
            public Duration getDuration() {
               return d;
            }
         };
      }

      static ParseResult2 lessThanOneMilli() {
         return new ParseResult2() {
            @Override
            public boolean isLessThanOneMilli() {
               return true;
            }

            @Override
            public Duration getDuration() {
               throw new IllegalStateException();
            }
         };
      }
   }

Itu menjadi lebih banyak kode, tetapi Anda hanya perlu menulisnya sekali, dan (dengan asumsi Anda mendokumentasikan hal-hal dengan benar), orang-orang yang akhirnya menggunakan kode Anda tidak harus menebak apa artinya hasilnya, dan tidak dapat secara tidak sengaja melakukan hal-hal seperti result[0]ketika itu berarti result[1]. Anda masih dapat membuat instance dengan cukup ringkas, dan mendapatkan data dari situ juga tidak terlalu sulit:

  ParseResult2 x = ParseResult2.from(TimeUnit.MILLISECONDS, 32);
  ParseResult2 y = ParseResult2.lessThanOneMilli();

Perhatikan bahwa Anda benar-benar dapat melakukan sesuatu seperti ini dengan pendekatan berbasis kelas juga. Cukup tentukan konstruktor untuk kasus yang berbeda. Anda masih memiliki masalah apa yang harus diinisialisasi dengan bidang lain, dan Anda tidak dapat mencegah akses ke sana.

Jawaban lain menyebutkan bahwa sifat Java tipe perusahaan berarti bahwa sebagian besar waktu, Anda membuat perpustakaan lain yang sudah ada, atau menulis perpustakaan untuk digunakan orang lain. API publik Anda seharusnya tidak memerlukan banyak waktu berkonsultasi dengan dokumentasi untuk menguraikan jenis hasil jika dapat dihindari.

Anda hanya menulis struktur ini satu kali, tetapi Anda membuatnya berkali-kali, jadi Anda tetap menginginkan penciptaan yang ringkas (yang Anda dapatkan). Pengetikan statis memastikan bahwa data yang Anda peroleh darinya adalah yang Anda harapkan.

Sekarang, semua yang dikatakan, masih ada tempat-tempat di mana tuple atau daftar sederhana bisa masuk akal. Mungkin ada lebih sedikit overhead dalam mengembalikan array sesuatu, dan jika itu masalahnya (dan overhead itu signifikan, yang akan Anda tentukan dengan profiling), maka menggunakan array nilai sederhana secara internal mungkin membuat banyak akal. API publik Anda mungkin masih memiliki jenis yang jelas.


Saya telah menggunakan enum saya sendiri alih-alih TimeUnit pada akhirnya, karena saya baru saja menambahkan UNDER1MS bersama dengan unit waktu aktual. Jadi sekarang saya hanya memiliki dua bidang, bukan tiga. (Secara teori, saya bisa menyalahgunakan TimeUnit.NANOSECONDS, tapi itu akan sangat membingungkan.)
Mikhail Ramendik

Saya tidak membuat pengambil, tetapi sekarang saya melihat bagaimana pengambil akan memungkinkan saya untuk melemparkan pengecualian jika, dengan originalTimeUnit=DurationTimeUnit.UNDER1MS, penelepon mencoba membaca nilai milidetik.
Mikhail Ramendik

Saya tahu Anda telah menyebutkannya, tapi ingat contoh Anda dengan tupel python benar-benar tentang tupel dinamis vs yang diketik secara statis; itu tidak benar-benar mengatakan banyak tentang tuple sendiri. Anda dapat memiliki tupel yang diketik secara statis yang akan gagal dikompilasi, karena saya yakin Anda tahu :)
Andres F.

1
@ Veedrac Saya tidak yakin apa yang Anda maksud. Maksud saya adalah tidak apa-apa untuk menulis kode pustaka lebih secara verbal seperti ini (walaupun itu membutuhkan waktu lebih lama), karena akan ada lebih banyak waktu menggunakan kode daripada menulisnya .
Joshua Taylor

1
@Veedrac Ya, itu benar. Tapi aku mempertahankan bahwa ada adalah perbedaan antara kode yang akan bermakna digunakan kembali, dan kode yang tidak akan. Misalnya, dalam paket Java, beberapa kelas bersifat publik, dan terbiasa dari lokasi lain. API tersebut harus dipikirkan dengan baik. Secara internal, ada kelas yang mungkin hanya perlu berbicara satu sama lain. Kopling internal tersebut adalah tempat di mana struktur cepat dan kotor (mis., Obyek [] yang diharapkan memiliki tiga elemen) mungkin baik-baik saja. Untuk API publik, sesuatu yang lebih bermakna dan eksplisit biasanya lebih disukai.
Joshua Taylor

7

Masalahnya adalah Anda membandingkan apel dengan jeruk . Anda bertanya bagaimana mensimulasikan pengembalian lebih dari nilai tunggal memberikan contoh python cepat & kotor dengan tuple yang tidak diketik dan Anda benar-benar menerima jawaban praktis satu-baris .

Jawaban yang diterima memberikan solusi bisnis yang benar. Tidak ada solusi sementara cepat yang harus Anda buang dan implementasikan dengan benar saat pertama kali Anda perlu melakukan sesuatu yang praktis dengan nilai yang dikembalikan, tetapi kelas POJO yang kompatibel dengan sejumlah besar perpustakaan, termasuk persistensi, serialisasi / deserialisasi, instrumentasi dan apa pun yang mungkin.

Ini juga tidak lama sama sekali. Satu-satunya hal yang perlu Anda tulis adalah definisi bidang. Setter, getter, kode hash, dan sederajat dapat dihasilkan. Jadi pertanyaan Anda yang sebenarnya seharusnya, mengapa getter dan setter tidak dihasilkan secara otomatis tetapi ini adalah masalah sintaksis (masalah gula sintaksis, menurut beberapa orang) dan bukan masalah budaya.

Dan akhirnya, Anda terlalu banyak berpikir untuk mempercepat sesuatu yang tidak penting sama sekali. Waktu yang dihabiskan untuk menulis kelas DTO tidak signifikan dibandingkan dengan waktu yang dihabiskan untuk memelihara dan men-debug sistem. Oleh karena itu tidak ada yang mengoptimalkan untuk verbositas kurang.


2
"Tidak ada yang" secara faktual salah - ada banyak alat yang mengoptimalkan untuk lebih sedikit verbositas, ekspresi reguler adalah contoh ekstrem. Tapi Anda mungkin berarti "tidak ada di dunia Jawa arus utama", dan dalam membaca jawaban itu sangat masuk akal, terima kasih. Perhatian saya sendiri dengan verbositas bukan waktu yang dihabiskan untuk menulis kode tetapi waktu yang dihabiskan untuk membacanya; IDE tidak akan menghemat waktu membaca; tetapi tampaknya idenya adalah bahwa 99% dari waktu seseorang hanya membaca definisi antarmuka sehingga BAHWA harus singkat?
Mikhail Ramendik

5

Tiga faktor berbeda yang berkontribusi pada apa yang Anda amati.

Tuples versus bidang bernama

Mungkin yang paling sepele - dalam bahasa lain Anda menggunakan tuple. Memperdebatkan apakah tuple adalah ide yang bagus bukan intinya - tetapi di Jawa Anda memang menggunakan struktur yang lebih berat sehingga perbandingannya sedikit tidak adil: Anda bisa menggunakan array objek dan beberapa tipe casting.

Sintaks bahasa

Mungkinkah lebih mudah untuk mendeklarasikan kelas? Saya tidak berbicara tentang membuat bidang menjadi publik atau menggunakan peta tetapi sesuatu seperti kelas kasus Scala, yang memberikan semua manfaat dari pengaturan yang Anda uraikan tetapi jauh lebih ringkas:

case class Foo(duration: Int, unit: String, tooShort: Boolean)

Kita dapat memiliki itu - tetapi ada biaya: sintaks menjadi lebih rumit. Tentu saja mungkin layak untuk beberapa kasus, atau bahkan untuk sebagian besar kasus, atau bahkan untuk sebagian besar kasus selama 5 tahun ke depan - tetapi perlu dinilai. Ngomong-ngomong, ini merupakan salah satu hal menyenangkan dengan bahasa yang dapat Anda modifikasi sendiri (yaitu lisp) - dan perhatikan bagaimana hal ini menjadi mungkin karena kesederhanaan sintaksis. Bahkan jika Anda tidak benar-benar mengubah bahasa, sintaksis sederhana memungkinkan alat yang lebih kuat; misalnya banyak kali saya kehilangan beberapa opsi refactoring yang tersedia untuk Java tetapi tidak untuk Scala.

Filosofi bahasa

Tetapi faktor yang paling penting adalah bahwa bahasa harus memungkinkan cara berpikir tertentu. Kadang-kadang mungkin terasa menindas (saya sering berharap untuk dukungan untuk fitur tertentu) tetapi menghapus fitur sama pentingnya dengan memilikinya. Bisakah Anda mendukung semuanya? Tentu, tetapi kemudian Anda mungkin juga menulis kompiler yang mengkompilasi setiap bahasa. Dengan kata lain, Anda tidak akan memiliki bahasa - Anda akan memiliki superset bahasa dan setiap proyek pada dasarnya akan mengadopsi subset.

Tentu saja mungkin untuk menulis kode yang bertentangan dengan filosofi bahasa, dan, seperti yang Anda amati, hasilnya seringkali jelek. Memiliki kelas dengan hanya beberapa bidang di Jawa mirip dengan menggunakan var di Scala, menurunkan predikat prolog ke fungsi, melakukan Performa yang tidak aman di haskell dll. Kelas Java tidak dimaksudkan untuk menjadi ringan - mereka tidak ada di sana untuk melewatkan data di sekitar . Ketika sesuatu terasa sulit, sering kali bermanfaat untuk mundur dan melihat apakah ada cara lain. Dalam contoh Anda:

Mengapa durasinya terpisah dari unit? Ada banyak pustaka waktu yang memungkinkan Anda mendeklarasikan durasi - sesuatu seperti Durasi (5, detik) (sintaks akan bervariasi), yang kemudian akan membiarkan Anda melakukan apa pun yang Anda inginkan dengan cara yang jauh lebih kuat. Mungkin Anda ingin mengonversinya - mengapa memeriksa apakah hasil [1] (atau [2]?) Adalah 'jam' dan dikalikan dengan 3600? Dan untuk argumen ketiga - apa tujuannya? Saya kira bahwa pada titik tertentu Anda harus mencetak "kurang dari 1 ms" atau waktu aktual - itu adalah beberapa logika yang secara alami dimiliki oleh data waktu. Yaitu Anda harus memiliki kelas seperti ini:

class TimeResult {
    public TimeResult(duration, unit, tooShort)
    public String getResult() {
        if tooShort:
           return "too short"
        else:
           return format(duration)
}

}

atau apa pun yang sebenarnya ingin Anda lakukan dengan data, maka enkapsulasi logikanya.

Tentu saja, mungkin ada kasus di mana cara ini tidak akan berhasil - Saya tidak mengatakan bahwa ini adalah algoritma ajaib untuk mengubah hasil tuple ke kode Java idiomatik! Dan mungkin ada kasus di mana itu sangat jelek dan buruk dan mungkin Anda harus menggunakan bahasa yang berbeda - itu sebabnya ada begitu banyak!

Tapi pandangan saya tentang mengapa kelas adalah "struktur berat" di Jawa adalah bahwa Anda tidak dimaksudkan untuk menggunakannya sebagai wadah data tetapi sebagai sel logika yang mandiri.


Apa yang ingin saya lakukan dengan durasi sebenarnya tentang membuat jumlah mereka dan kemudian membandingkan dengan yang lain. Tidak ada output yang terlibat. Perbandingan perlu memperhitungkan, jadi saya perlu tahu satuan waktu asli pada saat perbandingan.
Mikhail Ramendik

Mungkin akan mungkin untuk membuat cara menambah dan membandingkan contoh kelas saya, tetapi ini bisa menjadi sangat berat. Terutama karena saya juga perlu membagi dan mengalikan dengan float (ada persentase untuk memverifikasi di UI itu). Jadi untuk sekarang saya membuat kelas yang tidak berubah dengan bidang terakhir, yang tampak seperti kompromi yang bagus.
Mikhail Ramendik

Bagian yang lebih penting tentang pertanyaan khusus ini adalah sisi yang lebih umum, dan dari semua jawaban, sebuah gambar tampaknya menyatu.
Mikhail Ramendik

@MikhailRamendik mungkin semacam akumulator akan menjadi pilihan - tetapi Anda tahu masalahnya lebih baik. Memang ini bukan tentang menyelesaikan masalah khusus ini, maaf jika saya sedikit tersesat - poin utama saya adalah bahwa bahasa tersebut tidak mendukung penggunaan data "sederhana". kadang-kadang, ini membantu Anda memikirkan kembali pendekatan Anda - di lain waktu int hanya int
Thanos Tintinidis

@MikhailRamendik Hal yang menyenangkan tentang cara boilerplatey adalah bahwa sekali Anda memiliki kelas, Anda dapat menambahkan perilaku ke dalamnya. Dan / atau membuatnya tidak berubah seperti yang Anda lakukan (ini adalah ide yang bagus sebagian besar waktu; Anda selalu dapat mengembalikan objek baru sebagai jumlah). Pada akhirnya, kelas "berlebihan" Anda mungkin merangkum semua perilaku yang Anda butuhkan dan kemudian biaya untuk getter dapat diabaikan. Selain itu, Anda mungkin atau mungkin tidak membutuhkannya. Pertimbangkan untuk menggunakan lombok atau nilai otomatis .
maaartinus

5

Menurut pemahaman saya, alasan intinya adalah

  1. Antarmuka adalah cara dasar Jawa untuk abstrak kelas jauh.
  2. Java hanya dapat mengembalikan nilai tunggal dari metode - objek atau array atau nilai asli (int / long / double / float / boolean).
  3. Antarmuka tidak dapat berisi bidang, hanya metode. Jika Anda ingin mengakses bidang, Anda harus melalui metode - karenanya getter dan setter.
  4. Jika suatu metode mengembalikan antarmuka, Anda harus memiliki kelas pelaksana untuk benar-benar kembali.

Ini memberi Anda "Anda harus menulis kelas untuk kembali untuk hasil yang tidak sepele" yang pada gilirannya agak berat. Jika Anda menggunakan kelas alih-alih antarmuka, Anda bisa memiliki bidang dan menggunakannya secara langsung, tetapi itu mengikat Anda ke implementasi tertentu.


Untuk menambah ini: jika Anda hanya membutuhkan ini dalam konteks kecil (katakanlah, metode pribadi di kelas), tidak apa-apa menggunakan catatan kosong - idealnya tidak berubah. Tapi itu benar-benar benar-benar tidak boleh bocor ke kontrak publik kelas (atau lebih buruk lagi, perpustakaan!). Anda dapat memutuskan terhadap catatan hanya untuk memastikan bahwa ini tidak pernah terjadi dengan perubahan kode di masa depan, tentu saja; seringkali merupakan solusi yang lebih sederhana.
Luaan

Dan saya setuju dengan "Tuple tidak berfungsi dengan baik di Jawa" -view. Jika Anda menggunakan obat generik, ini akan sangat cepat menjadi tidak menyenangkan.
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Mereka bekerja cukup baik di Scala, bahasa yang diketik secara statis dengan generik dan tupel. Jadi mungkin ini salah Jawa?
Andres F.

@AndresF. Harap perhatikan bagian "Jika Anda menggunakan obat generik". Cara Java melakukan ini tidak cocok untuk konstruksi kompleks yang bertentangan dengan misalnya Haskell. Saya tidak tahu Scala cukup baik untuk melakukan perbandingan, tetapi apakah benar Scala memungkinkan untuk beberapa nilai pengembalian? Itu mungkin banyak membantu.
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Scala fungsi memiliki nilai kembali tunggal yang mungkin dari jenis tuple (misalnya def f(...): (Int, Int)adalah fungsi fyang mengembalikan nilai yang kebetulan menjadi tupel bilangan bulat). Saya tidak yakin obat generik di Jawa adalah masalah dengan itu; perhatikan bahwa Haskell juga melakukan penghapusan jenis, misalnya. Saya tidak berpikir ada alasan teknis mengapa Java tidak memiliki tupel.
Andres F.

3

Saya setuju dengan jawaban JacquesB itu

Java (secara tradisional) adalah bahasa OO berbasis paradigma kelas tunggal yang mengoptimalkan untuk kejelasan dan konsistensi

Tetapi kesederhanaan dan konsistensi bukanlah tujuan akhir untuk dioptimalkan. Ketika Anda mengatakan 'python dioptimalkan untuk keterbacaan', Anda segera menyebutkan bahwa tujuan akhirnya adalah 'pemeliharaan' dan 'kecepatan pengembangan'.

Apa yang Anda capai ketika Anda memiliki kesaksian dan konsistensi, dilakukan dengan cara Jawa? Menurut saya itu berkembang sebagai bahasa yang mengklaim untuk memberikan cara yang diprediksi, konsisten, seragam untuk menyelesaikan masalah perangkat lunak.

Dengan kata lain, budaya Java dioptimalkan untuk membuat manajer percaya bahwa mereka memahami pengembangan perangkat lunak.

Atau, seperti yang dikatakan oleh orang bijak sejak dulu ,

Cara terbaik untuk menilai suatu bahasa adalah dengan melihat kode yang ditulis oleh para pendukungnya. "Radix enim omnium malorum est cupiditas" - dan Java jelas merupakan contoh pemrograman berorientasi uang (MOP). Seperti yang dikatakan kepala pendukung Java di SGI: "Alex, Anda harus pergi ke tempat uang itu berada." Tapi saya tidak terlalu ingin pergi ke tempat uang itu - biasanya tidak berbau enak di sana.


3

(Jawaban ini bukan penjelasan untuk Java khususnya, tetapi menjawab pertanyaan umum "Apa yang mungkin mengoptimalkan praktik [berat]?")

Pertimbangkan dua prinsip ini:

  1. Ada baiknya ketika program Anda melakukan hal yang benar. Kita harus membuatnya mudah untuk menulis program yang melakukan hal yang benar.
  2. Ini buruk ketika program Anda melakukan hal yang salah. Kita harus membuatnya lebih sulit untuk menulis program yang melakukan hal yang salah.

Mencoba untuk mengoptimalkan salah satu dari tujuan-tujuan ini kadang-kadang dapat menghalangi yang lain (yaitu membuatnya lebih sulit untuk melakukan hal yang salah juga dapat membuat lebih sulit untuk melakukan hal yang benar , atau sebaliknya).

Pengorbanan yang dibuat dalam kasus tertentu tergantung pada aplikasi, keputusan dari programmer atau tim yang bersangkutan, dan budaya (dari organisasi atau komunitas bahasa).

Misalnya, jika bug atau penghentian beberapa jam dalam program Anda dapat mengakibatkan hilangnya nyawa (sistem medis, aeronautika) atau bahkan hanya uang (seperti jutaan dolar di katakan sistem iklan Google), Anda akan membuat pengorbanan yang berbeda (tidak hanya dalam bahasa Anda, tetapi juga dalam aspek lain dari budaya rekayasa) daripada yang Anda lakukan untuk skrip satu kali: kemungkinan condong ke sisi "berat".

Contoh lain yang cenderung membuat sistem Anda lebih "berat":

  • Ketika Anda memiliki basis kode besar yang dikerjakan oleh banyak tim selama bertahun-tahun, satu kekhawatiran besar adalah bahwa seseorang dapat menggunakan API orang lain secara tidak benar. Suatu fungsi dipanggil dengan argumen dalam urutan yang salah, atau dipanggil tanpa memastikan beberapa prasyarat / kendala yang diharapkannya, bisa menjadi bencana besar.
  • Sebagai kasus khusus ini, katakanlah tim Anda mengelola API atau pustaka tertentu, dan ingin mengubahnya atau membuatnya kembali. Semakin "terbatas" pengguna Anda dalam hal bagaimana mereka dapat menggunakan kode Anda, semakin mudah untuk mengubahnya. (Perhatikan bahwa akan lebih baik di sini untuk memiliki jaminan aktual di sini bahwa tidak ada yang bisa menggunakannya dengan cara yang tidak biasa.)
  • Jika pengembangan dibagi di antara beberapa orang atau tim, mungkin ide yang bagus untuk membuat satu orang atau tim "menentukan" antarmuka, dan meminta orang lain benar-benar mengimplementasikannya. Agar itu berfungsi, Anda harus dapat memperoleh tingkat kepercayaan ketika implementasi dilakukan, bahwa implementasi benar-benar sesuai dengan spesifikasi.

Ini hanya beberapa contoh, untuk memberi Anda gambaran tentang kasus di mana membuat hal-hal "berat" (dan membuatnya lebih sulit bagi Anda untuk hanya menulis beberapa kode dengan cepat) mungkin benar-benar disengaja. (Orang mungkin bahkan berpendapat bahwa jika menulis kode memerlukan banyak usaha, itu mungkin membuat Anda berpikir lebih hati-hati sebelum menulis kode! Tentu saja argumen ini dengan cepat menjadi konyol.)

Contoh: Sistem Python internal Google cenderung membuat hal-hal “berat” sehingga Anda tidak bisa hanya mengimpor kode orang lain, Anda harus mendeklarasikan ketergantungan pada file BUILD , tim yang kode yang ingin Anda impor perlu perpustakaannya dinyatakan sebagai pustaka mereka. terlihat oleh kode Anda, dll.


Catatan : Semua hal di atas hanya tentang saat segala sesuatu cenderung menjadi "berat". Saya benar-benar tidak mengklaim bahwa Java atau Python (baik bahasa itu sendiri, atau budaya mereka) membuat tradeoff optimal untuk kasus tertentu; itu untuk Anda pikirkan. Dua tautan terkait pada pengorbanan tersebut:


1
Bagaimana dengan membaca kode? Ada pengorbanan lain di sana. Kode yang dibebani oleh mantra aneh dan ritual yang banyak lebih sulit dibaca; dengan teks boilerplate dan yang tidak perlu (dan hierarki kelas!) di IDE Anda, menjadi lebih sulit untuk melihat apa yang sebenarnya dilakukan kode. Saya dapat melihat argumen bahwa kode tidak harus mudah untuk ditulis (tidak yakin apakah saya setuju dengan itu, tetapi memiliki beberapa kelebihan), tetapi pasti harus mudah dibaca . Jelas kode yang kompleks dapat dan telah dibangun dengan Java - itu akan bodoh untuk mengklaim sebaliknya - tetapi apakah itu berkat verbositasnya, atau terlepas dari itu?
Andres F.

@AndresF. Saya mengedit jawaban untuk membuatnya lebih jelas ini bukan tentang Jawa secara khusus (yang saya bukan penggemar berat). Tapi ya, timbal balik saat membaca kode: di satu sisi Anda ingin dapat dengan mudah membaca "apa yang sebenarnya dilakukan kode" seperti yang Anda katakan. Di ujung lain, Anda ingin dapat dengan mudah melihat jawaban atas pertanyaan seperti: bagaimana hubungan kode ini dengan kode lain? Apa yang terburuk yang bisa dilakukan oleh kode ini: apa dampaknya terhadap negara lain, apa efek sampingnya? Faktor eksternal apa yang mungkin menjadi sandaran kode ini? (Tentu saja idealnya kita menginginkan jawaban cepat untuk kedua set pertanyaan.)
ShreevatsaR

2

Budaya Jawa telah berkembang seiring waktu dengan pengaruh besar baik dari sumber terbuka maupun latar belakang perangkat lunak perusahaan - yang merupakan campuran aneh jika Anda benar-benar memikirkannya. Solusi perusahaan menuntut alat berat, dan sumber terbuka menuntut kesederhanaan. Hasil akhirnya adalah bahwa Jawa ada di suatu tempat di tengah.

Bagian dari apa yang mempengaruhi rekomendasi adalah apa yang dianggap dapat dibaca dan dipelihara dengan Python dan Java sangat berbeda.

  • Dalam Python, tuple adalah fitur bahasa .
  • Dalam Java dan C #, tuple adalah (atau akan) fitur perpustakaan .

Saya hanya menyebutkan C # karena perpustakaan standar memiliki satu set kelas Tuple <A, B, C, .. n>, dan ini adalah contoh sempurna tentang seberapa sulit tuple jika bahasa tidak mendukungnya secara langsung. Dalam hampir setiap contoh, kode Anda menjadi lebih mudah dibaca dan dipelihara jika Anda telah memilih kelas dengan baik untuk menangani masalah ini. Dalam contoh spesifik dalam pertanyaan Stack Overflow tertaut Anda, nilai-nilai lain akan dengan mudah dinyatakan sebagai getter yang dihitung pada objek kembali.

Solusi menarik yang dilakukan oleh platform C # yang memberikan jalan tengah yang bahagia adalah gagasan tentang objek anonim (dirilis dalam C # 3.0 ) yang menggaruk gatal ini dengan cukup baik. Sayangnya, Java belum memiliki yang setara.

Sampai fitur bahasa Java diubah, solusi yang paling mudah dibaca dan dipelihara adalah memiliki objek khusus. Itu karena kendala dalam bahasa yang berasal dari awal pada tahun 1995. Para penulis asli memiliki banyak fitur bahasa yang direncanakan yang tidak pernah berhasil, dan kompatibilitas ke belakang adalah salah satu kendala utama seputar evolusi Jawa dari waktu ke waktu.


4
Dalam versi terbaru dari c # tupel baru adalah warga negara kelas satu dan bukan kelas perpustakaan. Butuh terlalu banyak versi untuk sampai ke sana.
Igor Soloydenko

3 paragraf terakhir dapat diringkas sebagai "Bahasa X memiliki fitur yang Anda cari yang tidak dimiliki Java", dan saya gagal melihat apa yang mereka tambahkan pada jawabannya. Mengapa menyebutkan C # dalam topik Python ke Java? Tidak, saya hanya tidak mengerti intinya. Agak aneh dari pria 20k + rep.
Olivier Grégoire

1
Seperti yang saya katakan, "Saya hanya menyebutkan C # karena Tuples adalah fitur perpustakaan" mirip dengan bagaimana itu akan bekerja di Jawa jika memiliki Tuples di perpustakaan utama. Sangat sulit digunakan, dan jawaban yang lebih baik hampir selalu memiliki kelas khusus. Objek anonim menggaruk gatal serupa untuk apa tuple dirancang untuk, meskipun. Tanpa dukungan bahasa untuk sesuatu yang lebih baik, Anda pada dasarnya harus bekerja dengan apa yang paling dapat dipertahankan.
Berin Loritsch

@IgorSoloydenko, mungkin ada harapan untuk Java 9? Ini hanyalah salah satu fitur yang merupakan langkah logis berikutnya untuk lambdas.
Berin Loritsch

1
@IgorSoloydenko Saya tidak yakin itu cukup benar (warga negara kelas satu) - mereka tampaknya merupakan ekstensi sintaksis untuk membuat dan membuka contoh kelas dari perpustakaan, alih-alih memiliki dukungan kelas pertama di CLR sebagai class, struct, enum do.
Pete Kirkham

0

Saya pikir salah satu hal inti tentang menggunakan kelas dalam hal ini adalah bahwa apa yang berjalan bersama harus tetap bersama.

Saya telah melakukan diskusi sebaliknya, tentang argumen metode: Pertimbangkan metode sederhana yang menghitung BMI:

CalculateBMI(weight,height)
{
  System.out.println("BMI: " + (( weight / height ) x 703));
}

Dalam hal ini saya akan menentang gaya ini karena berat dan tinggi terkait. Metode "berkomunikasi" itu adalah dua nilai yang terpisah ketika mereka tidak. Kapan Anda menghitung BMI dengan berat satu orang dan tinggi orang lain? Itu tidak masuk akal.

CalculateBMI(Person)
{
  System.out.println("BMI: " + (( Person.weight / Person.height ) x 703));
}

Lebih masuk akal karena sekarang Anda jelas berkomunikasi bahwa tinggi dan berat berasal dari sumber yang sama.

Hal yang sama berlaku untuk mengembalikan beberapa nilai. Jika mereka terhubung dengan jelas maka kembalikan paket kecil yang rapi dan gunakan objek, jika mereka tidak mengembalikan beberapa nilai.


4
Oke, tetapi anggap Anda memiliki tugas (hipotetis) untuk menunjukkan grafik: bagaimana BMI bergantung pada berat untuk beberapa ketinggian tetap tertentu. Kemudian, Anda tidak dapat melakukannya tanpa membuat Orang untuk setiap titik data. Dan, membawanya ekstrem, Anda tidak dapat membuat instance kelas Person tanpa tanggal lahir dan nomor paspor yang valid. Sekarang apa? Aku tidak downvote btw, ada yang alasan yang sah untuk bundling properti bersama-sama dalam beberapa kelas.
artem

1
@artem, itu adalah langkah berikutnya di mana antarmuka berperan. Jadi antarmuka orang yang beratnya bisa seperti itu. Anda terjebak dalam contoh yang saya berikan untuk menunjukkan bahwa apa yang berjalan bersama haruslah bersama.
Pieter B

0

Sejujurnya, budaya adalah bahwa programmer Java awalnya cenderung keluar dari universitas di mana prinsip-prinsip Berorientasi Objek dan prinsip-prinsip desain perangkat lunak yang berkelanjutan diajarkan.

Seperti yang dikatakan ErikE dalam lebih banyak kata dalam jawabannya, Anda sepertinya tidak menulis kode berkelanjutan. Apa yang saya lihat dari teladan Anda adalah ada masalah yang sangat canggung.

Dalam budaya Jawa, Anda akan cenderung mengetahui perpustakaan apa yang tersedia, dan itu akan memungkinkan Anda untuk mencapai lebih dari pemrograman Anda. Jadi Anda akan memperdagangkan keanehan Anda untuk pola desain dan gaya yang telah dicoba dan diuji dalam pengaturan industri inti.

Tapi seperti yang Anda katakan, ini bukan tanpa kerugian: hari ini, setelah menggunakan Java selama lebih dari 10 tahun, saya cenderung menggunakan Node / Javascript atau Go untuk proyek baru, karena keduanya memungkinkan pengembangan lebih cepat, dan dengan arsitektur gaya layanan-mikro ini adalah sudah cukup. Dilihat oleh fakta Google pertama kali banyak menggunakan Java, tetapi telah menjadi pencetus Go, saya kira mereka mungkin melakukan hal yang sama. Tetapi meskipun saya menggunakan Go dan Javascript sekarang, saya masih menggunakan banyak keterampilan desain yang saya dapatkan dari bertahun-tahun menggunakan dan memahami Java.


1
Khususnya, alat rekrutmen foobar yang digunakan google memungkinkan dua bahasa digunakan untuk solusi: java dan python. Saya merasa menarik bahwa Go bukan pilihan. Saya kebetulan pada presentasi ini di Go dan memiliki beberapa poin menarik tentang Java dan Python (serta bahasa lainnya.) Misalnya ada satu poin yang menonjol: "Mungkin sebagai akibatnya, programmer non-ahli bingung" kemudahan gunakan "dengan interpretasi dan pengetikan dinamis." Layak dibaca.
JimmyJames

@JimmyJames baru saja sampai di slide dengan mengatakan 'di tangan para ahli, mereka hebat' - ringkasan yang bagus dari masalah dalam pertanyaan
Tom
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.