Bagaimana cara saya mengelola seperangkat aturan dan angka ajaib yang sangat besar dalam program saya?

21

Saya agak baru dalam pemrograman (saya adalah insinyur mesin berdasarkan perdagangan), dan saya sedang mengembangkan sebuah program kecil selama waktu henti yang menghasilkan bagian (kerja keras) berdasarkan masukan dari berbagai orang dari sekitar pabrik.

Berdasarkan hanya beberapa input (tepatnya 6), saya perlu membuat ratusan panggilan API yang masing-masing dapat mengambil hingga selusin parameter; semua dihasilkan oleh seperangkat aturan yang telah saya kumpulkan setelah mewawancarai semua orang yang menangani bagian tersebut. Bagian aturan dan parameter dari kode saya adalah 250 baris dan terus bertambah.

Jadi, apa cara terbaik agar kode saya dapat dibaca dan dikelola? Bagaimana cara mengelompokkan semua angka ajaib saya, semua aturan, algoritma, dan bagian prosedural dari kode? Bagaimana cara saya berurusan dengan API yang sangat verbose dan granular?

Tujuan utama saya adalah untuk dapat menyerahkan sumber saya kepada seseorang dan membuat mereka mengerti apa yang saya lakukan, tanpa masukan saya.

pengguna2785724
sumber
7
Bisakah Anda memberikan beberapa contoh panggilan API ini?
Robert Harvey
"Semua masalah dalam ilmu komputer dapat diselesaikan dengan tingkat tipuan yang lain" - David Wheeler
Phil Frost
... kecuali terlalu banyak level tipuan :)
Dan Lyons
1
Sulit untuk menjawab pertanyaan Anda tanpa melihat kode Anda. Anda dapat memposting kode Anda di codereview.stackexchange.com dan mendapatkan saran dari programmer lain.
Gilbert Le Blanc

Jawaban:

26

Berdasarkan apa yang Anda gambarkan, Anda mungkin ingin menjelajahi dunia database yang indah. Kedengarannya seperti banyak angka ajaib yang Anda gambarkan - terutama jika mereka bagian yang bergantung - benar-benar data, bukan kode. Anda akan memiliki keberuntungan yang jauh lebih baik, dan akan jauh lebih mudah untuk memperpanjang aplikasi dalam jangka panjang, jika Anda dapat mengkategorikan bagaimana data terkait dengan bagian-bagian dan menentukan struktur database untuk itu.

Perlu diingat, 'database' tidak selalu berarti MySQL atau MS-SQL. Bagaimana Anda menyimpan data akan sangat tergantung pada bagaimana program digunakan, bagaimana Anda menulisnya, dll. Ini mungkin berarti database jenis SQL, atau mungkin berarti file teks yang diformat.

GrandmasterB
sumber
7
Setuju dengan mengodifikasi data dalam database, meskipun sepertinya dia memiliki masalah yang lebih besar.
Robert Harvey
Jika saya membuat program yang membuat bagian yang sama sekali berbeda, ya, ini akan menjadi jalan yang harus ditempuh. Hanya satu bagian dengan empat konfigurasi yang sedikit berbeda. Itu tidak akan pernah menjadi hal yang besar (kecuali jika mereka menyewa pengembang untuk membuat sesuatu seperti itu, dalam hal ini tidak masalah). Meskipun, saya kira itu akan menjadi pengalaman belajar yang hebat setelah saya selesai dan ingin refactor.
user2785724
1
Kedengarannya seperti soft coding . Basis data untuk keadaan bisa berubah. Angka ajaib tidak dapat berubah, menurut definisi.
Phil Frost
1
@ PhilFrost: Anda bisa membuatnya tidak berubah. Hanya saja, jangan menulis kepada mereka setelah pembuatan tabel awal.
Robert Harvey
1
@ PhilFrost: Yah, saya sekarang telah melihat API yang dia hadapi. Ini luar biasa hanya karena ukurannya yang tipis. Dia mungkin tidak membutuhkan database sama sekali, kecuali dia melakukannya.
Robert Harvey
14

Kecuali Anda mengantisipasi untuk memperluas ini ke beberapa bagian saya akan enggan untuk menambahkan database dulu. Memiliki basis data berarti banyak hal yang harus dipelajari untuk Anda, dan lebih banyak hal untuk dipasang agar bisa digunakan untuk orang lain. Menambahkan database tertanam membuat portable yang dapat dieksekusi akhir, tetapi seseorang dengan kode sumber Anda sekarang memiliki satu hal lagi untuk mulai bekerja.

Saya pikir daftar konstanta yang jelas namanya dan fungsi implementasi aturan akan banyak membantu. Jika Anda memberikan semua nama alami dan fokus pada teknik pemrograman melek Anda harus dapat membuat program yang dapat dibaca.

Idealnya Anda akan berakhir dengan kode yang mengatakan:

LeftBearingHoleDepth = BearingWidth + HoleDepthTolerance;
if (not CheckPartWidth(LeftBearingHoleDepth, {other parameters})
    {whatever you need to adjust}

Bergantung pada seberapa lokal konstantanya, saya akan tergoda untuk mendeklarasikannya dalam fungsi yang digunakan di mana memungkinkan. Cukup berguna untuk berbelok:

SomeAPICall(10,324.5, 1, 0.02, 6857);

ke

const NumberOfOilDrainHoles = 10
const OilDrainHoleSpacing = 324.5
{etc}
SomeAPICall(NumberOfOilDrainHoles, OilDrainHoleSpacing, {etc}

Itu memberi Anda sebagian besar kode dokumentasi diri dan juga mendorong siapa pun yang memodifikasi kode untuk memberikan nama yang bermakna sama dengan apa yang mereka tambahkan. Memulai lokal juga memudahkan untuk berurusan dengan jumlah konstanta yang akan Anda kumpulkan. Agak menyebalkan jika Anda harus terus menggulir daftar panjang konstanta untuk memastikan nilainya adalah yang Anda inginkan.

Satu tip untuk nama: letakkan kata paling penting di sebelah kiri. Mungkin tidak membaca dengan baik, tetapi membuat menemukan hal-hal lebih mudah. Sebagian besar waktu Anda melihat sebuah bah dan bertanya-tanya tentang baut, tidak melihat baut dan bertanya-tanya di mana baut itu, jadi sebut saja SumpBoltThreadPitch bukan BoltThreadPitchSump. Kemudian urutkan daftar konstanta. Kemudian, untuk mengekstrak semua titin thread Anda bisa mendapatkan daftar dalam editor teks dan menggunakan fungsi find, atau menggunakan alat seperti grep untuk mengembalikan hanya baris yang berisi "ThreadPitch".

MOo
sumber
1
juga mempertimbangkan membuat antarmuka Fluent
Ian
Inilah baris aktual dari kode saya. Apakah masuk akal apa yang terjadi di sini (argumen adalah x1, y1, z1, x2, y2, z2 sebagai ganda), jika Anda tahu apa arti nama variabel? .CreateLine(m_trunion_support_spacing / 2, -((m_flask_length / 2) + m_sand_ledge_width + m_wall_thickness), -m_flange_thickness, m_trunion_support_spacing / 2, -((m_flask_length / 2) + m_sand_ledge_width + m_wall_thickness), -m_flask_height + m_flange_thickness)
user2785724
Anda juga dapat menggunakan ctag dengan integrasi editor untuk menemukan konstanta.
Phil Frost
3
@ user2785724 Itu berantakan. Apa yang sedang dilakukannya? Apakah ia membuat alur dengan panjang dan kedalaman tertentu? Maka Anda bisa membuat fungsi yang disebut createGroove(length, depth). Anda perlu mengimplementasikan fungsi-fungsi yang menggambarkan apa yang ingin Anda capai sebagaimana Anda menggambarkannya kepada seorang insinyur mesin. Tentang apa pemrograman melek itu.
Phil Frost
Itulah panggilan API untuk menggambar satu garis dalam ruang 3d. Masing-masing dari 6 argumen berada pada baris yang berbeda dalam program. Seluruh API itu gila. Saya tidak tahu di mana harus membuat kekacauan, jadi saya membuatnya di sana. Jika Anda tahu apa panggilan API dan argumennya, Anda akan melihat apa titik akhir itu, menggunakan parameter yang Anda kenal, dan dapat menghubungkannya kembali ke bagian tersebut. Jika Anda ingin menjadi terbiasa dengan SolidWorks, API benar-benar labirin.
user2785724
4

Saya pikir pertanyaan Anda berkurang menjadi: bagaimana cara menyusun perhitungan? Harap perhatikan bahwa Anda ingin mengelola "seperangkat aturan", yang merupakan kode, dan "satu set angka ajaib", yang merupakan data. (Anda dapat melihatnya sebagai "data yang disematkan dalam kode Anda", namun tetap merupakan data).

Lebih jauh, membuat kode Anda "dapat dimengerti orang lain" sebenarnya adalah tujuan umum dari semua paradigma pemrograman (lihat misalnya " Pola Implementasi " oleh Kent Beck, atau " Kode Bersih " oleh Robert C. Martin untuk penulis pada perangkat lunak yang menyatakan tujuan yang sama seperti Anda, untuk program apa pun ).

Semua petunjuk dalam buku-buku ini akan berlaku untuk pertanyaan Anda. Biarkan saya mengekstrak beberapa petunjuk khusus untuk "angka ajaib" dan "set aturan":

  1. Gunakan konstanta dan Pencacahan bernama untuk mengganti angka ajaib

    Contoh konstanta :

    if (partWidth > 0.625) {
        // doSomeApiCall ...
    }
    return (partWidth - 0.625)
    

    harus diganti dengan konstanta bernama sehingga tidak ada perubahan yang dapat memperkenalkan kesalahan ketik dan memecah kode Anda, misalnya dengan mengubah yang pertama 0.625tetapi bukan yang kedua.

    const double MAX_PART_WIDTH = 0.625;
    
    if (partWidth > MAX_PART_WIDTH) {
        // doSomeApiCall ...
    }
    return (partWidth - MAX_PART_WIDTH)
    

    Contoh Pencacahan :

    Pencacahan dapat membantu Anda mengumpulkan data milik bersama. Jika Anda menggunakan Java, ingatlah bahwa Enums adalah objek; elemen mereka dapat menyimpan data, dan Anda dapat menentukan metode yang mengembalikan semua elemen, atau memeriksa beberapa properti. Di sini Enum digunakan dalam membangun Enum lain:

    public enum EnginePart {
        CYLINDER (100, Materials.STEEL),
        FLYWHEEL (120, Materials.STEEL),
        CRANKSHAFT (200, Materials.CARBON);
    
        private final double maxTemperature;
        private final Materials composition;
        private EnginePart(double maxTemperature, Materials composition) {
            this.maxTemperature = maxTemperature;
            this.composition = composition;
        }
    }
    
    public enum Materials {
        STEEL,
        CARBON
    }
    

    Keuntungannya adalah: sekarang tidak ada yang bisa salah mendefinisikan EnginePart yang tidak terbuat dari baja atau karbon, dan tidak ada yang bisa memperkenalkan EnginePart yang disebut "asdfasdf", seperti halnya jika itu adalah string yang akan diperiksa pada konten.

  2. The Strategi pola dan pola metode Pabrik menjelaskan bagaimana untuk merangkum "aturan" dan meneruskannya ke objek lain yang menggunakan mereka (dalam kasus pola Pabrik, penggunaan sedang membangun sesuatu; dalam kasus pola Strategi, yang penggunaan adalah apa pun yang Anda inginkan).

    Contoh pola metode Pabrik :

    Bayangkan Anda memiliki dua jenis Mesin: satu di mana setiap bagian memiliki harus terhubung ke Compressor, dan satu di mana setiap bagian dapat secara bebas terhubung ke apapun bagian lain. Diadaptasi dari Wikipedia

    public class EngineAssemblyLine {
        public EngineAssemblyLine() {
            EnginePart enginePart1 = makeEnginePart();
            EnginePart enginePart2 = makeEnginePart();
            enginePart1.connect(enginePart2);
            this.addEngine(engine1);
            this.addEngine(engine2);
        }
    
        protected Room makeEngine() {
            return new NormalEngine();
        }
    }
    

    Dan kemudian di kelas lain:

    public class CompressedEngineAssemblyLine extends EngineAssemblyLine {
        @Override
        protected Room makeRoom() {
            return new CompressedEngine();
        }
    }
    

    Bagian yang menarik adalah: sekarang konstruktor AssemblyLine Anda terpisah dari jenis Mesin apa yang ditangani. Mungkin addEnginemetode memanggil API jarak jauh ...

    Contoh pola Strategi :

    Pola Strategi menjelaskan cara memperkenalkan fungsi ke objek untuk mengubah perilakunya. Biarkan kami membayangkan Anda terkadang ingin memoles suatu bagian, kadang-kadang Anda ingin melukisnya, dan secara default Anda ingin meninjau kualitasnya. Ini adalah contoh Python, yang diadaptasi dari Stack Overflow

    class PartWithStrategy:
    
        def __init__(self, func=None) :
            if func:
                self.execute = func
    
        def execute(self):
            # ... call API of quality review ...
            print "Part will be reviewed"
    
    
    def polish():
        # ... call API of polishing department ...
        print "Part will be polished"
    
    
    def paint():
        # ... call API of painting department ...
        print "Part will be painted"
    
    if __name__ == "__main__" :
        strat0 = PartWithStrategy()
        strat1 = PartWithStrategy(polish)
        strat2 = PartWithStrategy(paint)
    
        strat0.execute()  # output is "Part will be reviewed"
        strat1.execute()  # output is "Part will be polished"
        strat2.execute()  # output is "Part will be painted"
    

    Anda dapat memperluas ini ke memegang daftar Tindakan yang ingin Anda lakukan, dan kemudian memanggil mereka dari executemetode. Mungkin generalisasi ini bisa lebih baik digambarkan sebagai pola Builder , tapi hei, kita tidak ingin pilih-pilih, kan? :)

logc
sumber
2

Anda mungkin ingin menggunakan mesin aturan. Mesin aturan memberi Anda DSL (Domain Specific Language) yang dirancang untuk memodelkan kriteria yang diperlukan untuk hasil tertentu dengan cara yang dapat dipahami, seperti yang dijelaskan dalam pertanyaan ini .

Bergantung pada implementasi mesin aturan, aturan bahkan dapat diubah tanpa mengkompilasi ulang kode. Dan karena aturan ditulis dalam bahasa mereka sendiri, bahasa yang sederhana, mereka dapat diubah oleh pengguna juga.

Jika Anda beruntung ada mesin aturan siap pakai untuk bahasa pemrograman yang Anda gunakan.

The downside adalah bahwa Anda harus berkenalan dengan mesin aturan yang mungkin sulit jika Anda seorang pemula pemrograman.

Zilluss
sumber
1

Solusi saya untuk masalah ini sangat berbeda: lapisan, pengaturan, dan LOP.

Pertama bungkus API dalam sebuah layer. Temukan urutan panggilan API yang digunakan bersama dan gabungkan ke dalam panggilan API Anda sendiri. Akhirnya tidak akan ada panggilan langsung ke API yang mendasarinya, hanya panggilan ke pembungkus Anda. Panggilan bungkus harus mulai terlihat seperti bahasa mini.

Kedua, terapkan 'pengaturan manajer'. Ini adalah cara untuk mengaitkan nama dengan nilai secara dinamis. Sesuatu seperti ini. Bahasa mini lainnya.

Baseplate.name="Base plate"
Baseplate.length=1032.5
Baseplate.width=587.3

Terakhir, terapkan bahasa mini Anda sendiri untuk mengekspresikan desain (ini adalah Pemrograman Berorientasi Bahasa). Bahasa ini harus dapat dimengerti oleh para insinyur dan desainer yang berkontribusi dalam aturan dan pengaturan. Contoh pertama dari produk semacam itu yang muncul di benak Anda adalah Gnuplot, tetapi ada banyak lainnya. Anda dapat menggunakan Python, meskipun secara pribadi saya tidak akan melakukannya.

Saya mengerti bahwa ini adalah pendekatan yang kompleks, dan mungkin berlebihan untuk masalah Anda, atau membutuhkan keterampilan yang belum Anda peroleh. Itu hanya bagaimana saya akan melakukannya.

david.pfx
sumber
0

Saya tidak yakin saya mendapatkan pertanyaan dengan benar, tetapi sepertinya Anda harus mengelompokkan beberapa hal dalam beberapa struktur. Katakanlah jika Anda menggunakan C ++, Anda dapat mendefinisikan hal-hal seperti:

struct SomeParametersClass
{
    int   p1;  // this is for that
    float p2;  // this is a different parameter
    ...
    SomeParametersClass() // constructor, assigns default values
    {
        p1 = 42; // the best value that some guy told me
        p2 = 3.14; // looks like a know value, but isn't
    {
};

struct SomeOtherParametersClass
{
    int   v1;  // this is for ...
    float v2;  // this is for ...
    ...
    SomeOtherParametersClass() // constructor, assigns default values
    {
        v1 = 24; // the best value 
        v2 = 1.23; // also the best value
    }
};

Anda dapat memulai ini di awal program:

int main()
{
    SomeParametersClass params1;
    SomeOtherParametersClass params2;
    ...

Maka panggilan API Anda akan terlihat (dengan asumsi Anda tidak dapat mengubah tanda tangan):

 SomeAPICall( params1.p1, params1.p2 );

Jika Anda dapat mengubah tanda tangan API, maka Anda dapat melewati seluruh struktur:

 SomeAPICall( params1 );

Anda juga dapat mengelompokkan semua parameter ke dalam pembungkus yang lebih besar:

struct AllTheParameters
{
    SomeParametersClass      SPC;
    SomeOtherParametersClass SOPC;
};
kebs
sumber
0

Saya terkejut bahwa tidak ada orang lain yang menyebutkan ini ...

Kamu berkata:

Tujuan utama saya adalah untuk dapat menyerahkan sumber saya kepada seseorang dan membuat mereka mengerti apa yang saya lakukan, tanpa masukan saya.

Jadi izinkan saya mengatakan ini, sebagian besar jawaban lain ada di jalur yang benar. Saya pasti berpikir bahwa database dapat membantu Anda. Tetapi hal lain yang akan membantu Anda adalah mengomentari, nama variabel yang baik, dan organisasi yang tepat / pemisahan masalah.

Semua jawaban lainnya sangat berbasis teknis, tetapi mereka mengabaikan dasar-dasar yang dipelajari kebanyakan programmer. Karena Anda seorang mech engie berdasarkan perdagangan, tebakan saya adalah Anda tidak terbiasa dengan gaya dokumentasi ini.

Mengomentari dan memilih nama variabel yang bagus dan ringkas sangat membantu keterbacaan. Mana yang lebih mudah dipahami?

var x = y + z;

Atau:

//Where bandwidth, which was previously defined is (1000 * Info Rate) / FEC Rate / Modulation * carrier spacing / 1000000
float endFrequency = centerFrequency + (1/2 bandwidth);

Ini bahasa yang mandiri. Tidak peduli apa platform, IDE, bahasa, dll. Anda bekerja dengan, dokumentasi yang tepat adalah cara terbersih dan termudah untuk memastikan seseorang dapat memahami kode Anda.

Selanjutnya adalah mengelola angka-angka ajaib dan banyak kekhawatiran, tapi saya pikir komentar GrandmasterB ditangani dengan cukup baik.

Drew
sumber