30 Хороший технарь и хороший программист

      Комментарии к записи 30 Хороший технарь и хороший программист отключены

Главная Форумы Программирование Программирование на С++ Заметки о С++ 30 Хороший технарь и хороший программист

Помечено: ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 2 мес., 3 нед. назад.

  • Автор
    Сообщения
  • #3608

    Хороший технический специалист практически никогда не бывает хорошим программистом. Живым подтверждением тому является бесчисленное множество ужасающих библиотек, сложных в реализации с технической точки зрения. В качестве примера таких библиотек можно рассмотреть математические библиотеки, библиотеки для кодирования и обработки изображений, кодирование потокового видео и аудио, криптографические библиотеки, и так далее. К сожалению, большинство технических специалистов (включая самых матерых экспертов) живут с иллюзией того, что для того, чтобы написать хорошую техническую библиотеку, достаточно быть хорошим техническим специалистом. К сожалению, этого недостаточно.

    Технический специалист сосредотачивает свои усилия на вопросах, непосредственно связанных с результатом работы библиотеки, а именно — на производительности и, если это имеет место, качестве выходных данных. При этом качество кода и вообще культура программирования нещадно приносятся в жертву результату, подобно тому, как крестоносцы вырезали половину Константинополя под предлогом «освобождения Гроба Господня».

    Новички в программировании ошибочно полагают, что они еще не доросли до некоторого SDK, если им не удалось понять, как этим SDK пользоваться. Новички, не расстраивайтесь — в действительности причина здесь ровно обратная — авторы SDK еще не доросли до того, чтобы писать SDK.

    Современные технические библиотеки — это грустно и печально. Обойдите все технические SDK, предоставляемые самыми крупными компаниями и организациями, от GNU до Microsoft, от NVIDIA до Intel, от Daz 3D до Autodesk — нигде, нигде вы не найдете ни одного SDK, ни одной библиотеки, ни одного модуля, хотя бы отдаленно напоминающего по качеству тот же Boost. Все что вы найдете — это брутальный говнокод, представляющий собой кровавый замес из процедурного C, недообъектного и недотипизированного C++, полнейшего непонимания паттернов проектирования, полного отсутствия вообще какой-либо культуры программирования и хотя бы элементарной заботы о собственном пользователе.

    Intel

    Компания Intel очень гордится своей библиотекой «Integrated Performance Primitives». Библиотека предоставляет функциональность по кодированию видео, обработке изображений, трехмерному рендерингу, и так далее. То есть популярные задачи, требующие хороших вычислительных мощностей. Казалось бы, кому как не Intel лучше всех справиться с этой задачей?

    Зайдя в папку «samples», мы увидим, что Intel любезно предоставила аж пять примеров работы с форматом JPEG/JPEG2000. Однако, наша радость становится полной, когда мы обнаружим, что размеры исходников примеров варьируются от 850-ти килобайт до 2-х мегабайт. Для сравнения — библиотека Boost.GIL, решающая гораздо более широкий круг задач, снабжена примерами размером в несколько сот байт, коротко и по сути объясняющими суть предоставляемой функциональности.

    Пример кричащего говнокода из библиотеки «Intel IPP», свидетельствующий о полнейшем дилетантстве технаря как программиста:

    template<class T,int N> struct IMG {
    private:
        T *img_mem; // ptr to allocated memory
    protected:
        T *img;     // holds current ROI start position (left-top point)
    public:
        int wstep;               // image pitch, set in constructor
        IPSize imgSize;          // memory image size, set in constructor
        IPSize roiSize;          // ROI, initially cover the whole image,
        IppStatus lastStatus;    // status of the last operation, where the image is a destination
    
        IMG(int width, int height) :
            imgSize(width,height),
            roiSize(width,height),
            lastStatus(ippStsNoOperation)
        {
            wstep = imgSize.width * N * sizeof(T);
            img = img_mem = (T*)ippiMalloc_8u_C1(wstep,imgSize.height,&wstep); // malloc in bytes
        }
    
        // moves left-top point of the image, modifies roiSize correspondently
        virtual void border(int wpixels,int hpixels)
        {
            img = (T*)((Ipp8u*)img+wstep*hpixels+wpixels*(N*sizeof(T)));
            roiSize.resize(-wpixels,-hpixels);
        }
        // modifies current roi rectangle - shifts left, top, right, bottom borders
        void roiRect(int addl, int addt, int addr, int addb)
        {
            border(addl, addt);
            roiSize.resize(addr, addb);
        }
        //restores ROI to the whole image
        virtual void restoreRect()
        {
            img = img_mem;
            roiSize = imgSize;
        }
    
        virtual ~IMG() { ippiFree(img_mem); }
    
        // returns current ROI start position (left-top point)
        inline T *ptr() const { return img; }
    };

    LEADTOOLS

    Компания LEADTOOLS со своего сайта заявляет, что она — «The World Leader in Imaging Development SDKs». Действительно, чем только они не занимаются — даже обслуживают такую серьезную отрасль как медицина. На деле же качество кода в SDK от LEADTOOLS точно также бросает в дрожь — точно такой же говнокод, такие же примеры использования в сотни килобайт, такое же отсутствие вообще какого-либо понимания о культуре программирования.

    Все примеры использования библиотеки LEADTOOLS представляют собой MFC-приложения, в которых 99,9% кода — это код MFC и 0,1% кода — это код самих LEADTOOLS (то есть именно тот код, ради которого пользователь и полез в пример). Без хороших познаний в области архитектуры построения MFC-проектов (кстати та еще трава), пользователь лишен всяких шансов научиться работать с изображением из примеров LEADTOOLS.

    Одни только заголовочные файлы LEADTOOLS не оставят равнодушным даже самого видавшего виды программиста:

    //
    // Вырезано 1500 аналогичных строк до
    //
    
    extern LWRP_EXPORT pL_GETLOADRESOLUTION   pL_GetLoadResolution;
    extern LWRP_EXPORT pL_GETFILECOMMENTSIZE  pL_GetFileCommentSize;
    extern LWRP_EXPORT pL_GETTAG  pL_GetTag;
    extern LWRP_EXPORT pL_LOADBITMAP pL_LoadBitmap;
    extern LWRP_EXPORT pL_LOADBITMAPLIST   pL_LoadBitmapList;
    extern LWRP_EXPORT pL_LOADBITMAPMEMORY pL_LoadBitmapMemory;
    extern LWRP_EXPORT pL_LOADFILE   pL_LoadFile;
    extern LWRP_EXPORT pL_LOADFILETILE  pL_LoadFileTile;
    extern LWRP_EXPORT pL_LOADMEMORYTILE   pL_LoadMemoryTile;
    extern LWRP_EXPORT pL_LOADFILEOFFSET   pL_LoadFileOffset;
    extern LWRP_EXPORT pL_LOADMEMORY pL_LoadMemory;
    extern LWRP_EXPORT pL_READFILECOMMENT  pL_ReadFileComment;
    extern LWRP_EXPORT pL_READFILECOMMENTEXT  pL_ReadFileCommentExt;
    extern LWRP_EXPORT pL_READFILECOMMENTMEMORY  pL_ReadFileCommentMemory;
    extern LWRP_EXPORT pL_READFILETAG   pL_ReadFileTag;
    extern LWRP_EXPORT pL_READFILETAGMEMORY   pL_ReadFileTagMemory;
    extern LWRP_EXPORT pL_READFILESTAMP pL_ReadFileStamp;
    extern LWRP_EXPORT pL_SAVEBITMAP pL_SaveBitmap;
    extern LWRP_EXPORT pL_SAVEBITMAPBUFFER pL_SaveBitmapBuffer;
    extern LWRP_EXPORT pL_SAVEBITMAPLIST   pL_SaveBitmapList;
    extern LWRP_EXPORT pL_SAVEBITMAPMEMORY pL_SaveBitmapMemory;
    extern LWRP_EXPORT pL_SAVEFILE   pL_SaveFile;
    extern LWRP_EXPORT pL_SAVEFILEBUFFER   pL_SaveFileBuffer;
    extern LWRP_EXPORT pL_SAVEFILEMEMORY   pL_SaveFileMemory;
    extern LWRP_EXPORT pL_SAVEFILETILE  pL_SaveFileTile;
    extern LWRP_EXPORT pL_SAVEFILEOFFSET   pL_SaveFileOffset;
    extern LWRP_EXPORT pL_SETCOMMENT pL_SetComment;
    extern LWRP_EXPORT pL_SETLOADINFOCALLBACK pL_SetLoadInfoCallback;
    extern LWRP_EXPORT pL_GETLOADINFOCALLBACKDATA   pL_GetLoadInfoCallbackData;
    extern LWRP_EXPORT pL_SETLOADRESOLUTION   pL_SetLoadResolution;
    extern LWRP_EXPORT pL_SETTAG  pL_SetTag;
    extern LWRP_EXPORT pL_STARTCOMPRESSBUFFER pL_StartCompressBuffer;
    extern LWRP_EXPORT pL_STARTFEEDLOAD pL_StartFeedLoad;
    extern LWRP_EXPORT pL_STOPFEEDLOAD  pL_StopFeedLoad;
    extern LWRP_EXPORT pL_WRITEFILECOMMENTEXT pL_WriteFileCommentExt;
    extern LWRP_EXPORT pL_WRITEFILESTAMP   pL_WriteFileStamp;
    extern LWRP_EXPORT pL_SETSAVERESOLUTION   pL_SetSaveResolution;
    extern LWRP_EXPORT pL_GETSAVERESOLUTION   pL_GetSaveResolution;
    extern LWRP_EXPORT pL_GETDEFAULTLOADFILEOPTION  pL_GetDefaultLoadFileOption;
    extern LWRP_EXPORT pL_GETDEFAULTSAVEFILEOPTION  pL_GetDefaultSaveFileOption;
    extern LWRP_EXPORT pL_WRITEFILETAG  pL_WriteFileTag;
    extern LWRP_EXPORT pL_WRITEFILECOMMENT pL_WriteFileComment;
    extern LWRP_EXPORT pL_CREATETHUMBNAILFROMFILE   pL_CreateThumbnailFromFile;
    extern LWRP_EXPORT pL_GETJ2KOPTIONS pL_GetJ2KOptions;
    extern LWRP_EXPORT pL_GETDEFAULTJ2KOPTIONS   pL_GetDefaultJ2KOptions;
    extern LWRP_EXPORT pL_SETJ2KOPTIONS pL_SetJ2KOptions;
    
    //
    // Вырезано 1500 аналогичных строк после
    //

    NVIDIA

    Библиотека PhysX от NVIDIA является еще одним живым подтверждением основного тезиса данной статьи. Конечно, непосредственно NVIDIA тут не при чем — PhysX был куплен компанией NVIDIA у компании AGEIA вместе с самой компанией AGEIA, и существует чуть более чем стопроцентная вероятность того, что PhysX был точно таким же говнокодом еще до его приобретения компанией NVIDIA.

    Работа над ошибками

    Если попытаться понять и разобраться, почему пример использования, скажем, кодера и декодера JPEG в исходниках занимает полтора мегабайта заглянув в эти самые исходники, то большая часть вещей встанет на свои места.

    Во-первых, каждых технарь в каждом примере пишет свой трехкилометровый класс для парсинга командной строки. Почему? Потому что он не знает про Boost.Program Options.

    Во-вторых, каждых технарь в каждом примере пишет свой парсер заголовков изображений в BMP-формате. Почему? Потому что он не знает про Boost.GIL.

    В третьих, каждых технарь в абсолютно каждом своем приложении изобретает еще очень много разных велосипедов с квадратными колесами, потому что он не знает про STL, контейнеры, итераторы, алгоритмы, функциональные объекты, а также библиотеки Boost, решающие огромный спектр задач.

    В четвертых, каждый технарь считает, что просто так взять и написать краткий и лаконичный консольный пример из 50-ти строк нельзя. Нужно обязательно создать MFC-приложение из нескольких десятков файлов и размазать по нему тонким слоем демонстрируемую функциональность, тем самым затруднив понимание сути предоставляемой функциональности на порядки. Видимо, технарям платят за строки кода.

    В пятых, технарь слишком сильно увлечен производительностью и качеством выходных данных, чтобы успевать думать о пользователях с их проблемами.

    В итоге 50 строк кода, которые могли бы наглядно продемонстрировать возможности библиотеки, превращаются в 50 000 строк абсолютно бессмысленной мешанины. Развитие среднестатистического технаря как программиста останавливается в тот момент, когда он научился писать программу, которая работает.

    Для того, чтобы написать хорошую библиотеку, недостаточно просто хорошо разбираться в предметной области. Хорошая библиотека — это не просто решение задачи. Хорошая библиотека — это удобное решение задачи. Практически ни один технарь не представляет себе, какая бездонная пропасть лежит между этими двумя понятиями. Интеграция удобной библиотеки в хороший проект — это редко минуты, чаще часы, редко дни. Интеграция неудобной библиотеки в хороший проект — это редко часы, чаще дни или недели. При этом на саму интеграцию тратятся все те же минуты или часы, а дни и недели уходят на изучение, сборку и адаптацию неудобной библиотеки под нужды проекта.

    Основная задача любого разработчика библиотеки (если не считать производительность и прочие очевидные вещи) — придумать удобный публичный интерфейс, то есть точку соприкосновения человека и библиотеки. Зачастую эта задача оказывается сложнее, нежели сама тематика предметной области задач, решаемых библиотекой. К сожалению, практически никто из технарей этого не понимает.

    Правило

    Хорошему технарю нужен хороший программист.

Для ответа в этой теме необходимо авторизоваться.