API DesignAPIWhy is API Design Important?Characteristics of Good APIsDesigning an APIBroad Issues to Consider in Design1. Interface PrinciplesSimpleGeneralRegularPredictableSlide 12RobustAdaptable2. Resource ManagementResource Management3. Error HandlingFail FastError ManagementExceptionsSlide 21API DesignCPSC 315 – Programming StudioSpring 2009Follows Kernighan and Pike, The Practice of Programming andJoshua Bloch’s Library-Centric Software Design ’05 Keynote Talk: ”How to Design a Good API and Why It Matters”APIApplication Programming InterfaceSource code interfaceFor library or OSProvides services to a programAt its base, like a header fileBut, more completeWhy is API Design Important?Company ViewCan be asset – big user investment in learning and usingBad design can be source of long-term support problemsOnce used, it’s tough to changeEspecially if there are several usersPublic APIs – One chance to get it rightCharacteristics of Good APIsEasy to learnEasy to use even without documentationHard to misuseEasy to read and maintain code that uses itSufficiently powerful to satisfy requirementsEasy to extendAppropriate to audienceDesigning an APIGather requirementsDon’t gather solutionsExtract true requirementsCollect specific scenarios where it will be usedCreate short specificationConsult with users to see whether it worksFlesh it out over timeHints:Write plugins/use examples before fully designed and implementedExpect it to evolveBroad Issues to Consider in Design1. InterfaceThe classes, methods, parameters, names2. Resource ManagementHow is memory, other resources dealt with3. Error HandlingWhat errors are caught and what is doneInformation HidingHow much detail is exposedImpacts all three of the above1. Interface PrinciplesSimpleGeneralRegularPredictableRobustAdaptableSimpleUsers have to understand!Do one thing and do it wellFunctionality should be easy to explainAs small as possible, but never smallerConceptual weight more important than providing all functionalityAvoid long parameter listsChoose small set of orthogonal primitivesDon’t provide 3 ways to do the same thingGeneralImplementation can change, API can’tHide Information!Don’t let implementation detail leak into APIMinimize accessibility (e.g. private classes and members)Implementation details can confuse usersBe aware of what is implementationDon’t overspecify behavior of modulesTuning parameters are suspectRegularDo the same thing the same way everywhereRelated things should be achieved by related meansConsistent parameter ordering, required inputsFunctionality (return types, errors, resource management)Names matterSelf explanatoryConsistent across APISame word means same thing in APISame naming style usedConsistent with related interfaces outside the APIPredictableDon’t violate the principle of Least AstonishmentUser should not be surprised by behaviorEven if this costs performanceDon’t reach behind the user’s backAccessing and modifying global variablesSecret files or information writtenBe careful about static variablesPredictableTry to minimize use of other interfacesMake as self-contained as possibleBe explicit about external services requiredDocument!Every class, method, interface, constructor, parameter, exceptionWhen states are kept, this should be very clearly documentedRobustAble to deal with unexpected inputError Handling (see later)AdaptableAPI can be extended, but never shortenedHeavily used APIs likely will be extendedInformation HidingImplementation details should not affect API2. Resource ManagementDetermine which side is responsible forInitializationMaintaining stateSharing and copyingCleaning upVarious resourcesMemoryFilesGlobal variablesResource ManagementGenerally, free resources where they were allocatedReturn references or copies?Can have huge performance and ease of use impactMulti-threaded code makes this especially criticalReentrant: works regardless of number of simultaneous executionsAvoid using anything (globals, static locals, other modifications) that others could also useLocks can be important3. Error HandlingCatch errors, don’t ignore them“Print message and fail” is not always goodEspecially in APIsNeed to allow programs to recover or save dataDetect at low level, but handle at high levelGenerally, error should be handled by calling routineThe callee can leave things in a “nice” state for recovery, thoughKeep things usable in case the caller can recoverFail FastReport as soon as an error occursSometimes even at compile time!Use of static types, genericsError ManagementReturn valuesShould be in form the calling function can useReturn as much useful information as possibleSentinel values only work if function cannot return all possible values of that typeDefine pairs, or return another parameter to indicate errorsUse error “wrapper function” if neededConsistent way of marking, reporting error statusEncourages useBut, can add complexityExceptionsGenerally indicate a programming errorProgramming constructSet exception value (e.g. as return)Other program operation when exception thrownExceptions usually in global registryInclude information about failureFor repair and debuggingExceptions should generally be uncheckedAutomatically process globally, rather than require explicit checks over and overExceptionsOnly use in truly exceptional situationsNever use as a control structureThe modern GOTONever use exceptions for expected return valuese.g. Invalid file name passed to library is “common”, not an
View Full Document