8. P-uppgift: prototyp

Programmeringsteknik

Hoppa till: navigering, sök

[redigera] Prototyp

När ditt kodskelett har blivit godkänt så är det dags att börja arbeta med en prototyp av programmet. Prototypen behöver inte implementera all funktionalitet. I biblioteksexemplet har vi valt att starta med inläsning och utskrift till fil. Vi har också lagt till de två första funktionerna i menyn: att söka på titel och att söka på författare.

När man jobbar med att skriva kod så upptäcker man ofta att den ursprungliga kodskelettet måste modifieras. Studera koden i exemplet nedan noggrannt och lägg märke till ändringarna.

  • Klassen Bibliotek har bytt namn till Catalog, ett namn som beskriver klassen bättre. Ett objekt av den här klassen representerar ju inte ett helt bibliotek utan bara en bibliotekskatalog.
  • Många av metoderna i Catalog-klassen har ändrats. När vi började skriva koden så märkte vi att många av de ursprungliga metoderna i klassen Bibliotek gjorde två helt skilda saker: de gjorde både operationer på biblioteksdatabasen samtidigt som de skötte det textbaserade användargränssnittet. En bättre lösning är att metoderna i klassen Catalog endast hanterar databasoperationerna. Användargränsnittet har vi implementerat separat med hjälp av ett antal funktioner.
  • Vi har nu bestämt hur data ska lagras på fil och varit noga med att dokumentera detta i programmet. Denna viktiga information saknades helt i kodskelettet.
  • Lägg märke till att det finns en extra utskrift (som inte ska vara med i det färdiga programmet) i huvudprogrammet. Just efter att vi har läst in data från filen och skapat en ny Catalog så skriver vi ut innehållet i listan books. Den här typen av testutskrifter är mycket användbara för att kolla att programmet fungerar som man har tänkt sig och för att leta efter eventuella fel.
  • När vi skrev koden så upptäckte vi ibland att vi upprepade samma kod på flera ställen. Det brukar vara en tydlig indikation på att det är lämpligt att introducera en ny funktion. Ett exempel är hjälpfunktionen are_equal som kollar om två strängar är lika så när som på små och stora bokstäver samt överflödiga blanktecken i början och slutet av strängarna. Den här funktionen la vi till eftersom samma kod dök upp i de båda funktionerna search_title och search_author.
  • Når vi skrev programmet så upptäckte vi att det fanns ett stort antal utskrifter som alla började med blanktecken: print ' text...'. För att slippa skriva om samma sak, en form av onödig hårdkodning, och för att lättare kunna ändra programmet i framtiden så införde vi istället konstanten INDENT = ' '.
  • Eftersom vi har planer på att göra extrauppgiften för betyg A, att lägga till ett grafiskt användargränssnitt, så har vi försökt att skilja ut den kod som hanterar kommunikationen till användaren från den kod som hanterar logiken för databasen. På det sättet blir det mycket enklare när vi ska byta ut textgränssnittet mot ett grafiskt gränssnitt. Målet är inte bara att få programmet att fungera, utan också att utforma det på ett sådant sätt att det är lätt att förstå och modifiera i framtiden.
# -*- coding: iso8859-1 -*-

# Titel: Bibliotek (prototyp)
# Författare: Stefan Nilsson
# Datum: 2007-06-29
#
# Det här är ett program för hantering av enklare biblioteksrutiner.
#
# Programmet lagrar böckerna i en fil med namnet "bibliotek.txt"
# mellan körningarna. Varje rad innehåller en bok beskriven på
# följande format:
#
#    <AUTHOR>/<TITLE>
#
# eller
#
#    <AUTHOR>/<TITLE>/Utlånad
#
# om boken är utlånad. Exempel på datafil:
#
#    Bälter/Programmeringsteknik med Python/Utlånad
#    Bälter/Pythonvetaren


# --------  En klass som beskriver en bok.  --------
# Attribut:
#    author - namnet på författaren
#    title - bokens titel
#    isBorrowed - en variabel som är True om boken är utlånad.
#
class Book:
    # Skapar en ny bok.
    def __init__(self, author, title):
        self.author = author
        self.title = title
        self.isBorrowed = False

    # Returnerar en sträng som beskriver boken.
    # Format: <author>: <title> (utlånad)
    def __str__(self):
        author_and_title = self.author + ': ' + self.title
        if self.isBorrowed:
            return author_and_title + ' (utlånad)'
        else:
            return author_and_title


# --------  En klass som beskriver en bibliotekskatalog.  --------
# Attribut:
#    books - en lista som innehåller samtliga böcker.
#
class Catalog:
    # Skapar en bibliotekskatalog med de böcker som finns i filen.
    def __init__(self, filename):
        self.books = list()
        file = open(filename, 'rU')
        for line in file:
            parts = line.strip().split('/')
            if (len(parts) >= 2):
                book = Book(parts[0], parts[1])
                if len(parts) == 3:
                    book.isBorrowed = True
                self.books.append(book)
        file.close()

    # Sparar hela bibliotekskatalogen i en fil.
    def save(self, filename):
        file = open(filename, 'wU')
        for book in self.books:
            file.write(book.author + '/' + book.title)
            if book.isBorrowed:
                file.write('/Utlånad')
            file.write('\n')
        file.close()

    # Returnerar boken om den finns, annars None.
    # Skiljer inte på små och stora bokstäver.
    def find_title(self, title):
        for book in self.books:
            if are_equal(title, book.title):
                return book
        return None

    # Returnerar en lista på böcker av en viss författare.
    # Return en tom lista om det inte finns några böcker.
    # Skiljer inte på stora och små bokstäver.
    def find_author(self, author):
        book_list = list()
        for book in self.books:
            if are_equal(author, book.author):
                book_list.append(book)
        return book_list


# --------  Hjälpfunktioner  --------
    
# Returnerar True om s1 och s2 är lika.
# Skiljer inte på små och stora bokstäver och tar bort
# eventuella blanktecken i början och slutet av strängen.
def are_equal(s1, s2):
    return s1.strip().upper() == s2.strip().upper()


# --------  Funktioner för textgränssnitt  --------

INDENT = '  ' # Tomma tecken i början av indragna rader

# Skriver ut valmenyn.
def print_menu():
    print INDENT, 'T  söka på Titel.'
    print INDENT, 'F  söka på Författare.'
    print INDENT, 'L  Låna bok.'
    print INDENT, 'Å  Återlämna bok.'
    print INDENT, 'N  lägga in Ny bok.'
    print INDENT, 'B  ta Bort bok.'
    print INDENT, 'A  lista Alla böcker.'
    print INDENT, 'S  Sluta.'

# Läser in och returnerar första bokstaven i användarens val.
# Omvandlar liten bokstav till stor.
def choose():
    choice = raw_input('Vad vill du göra? ')
    return choice[0].upper()

# Söker på titel.
def search_title(catalog):
    title = raw_input('Vilken titel vill du söka efter? ')
    book = catalog.find_title(title)
    if (book == None):
        print INDENT, 'Hittade ingen bok med den titeln.'
    else:
        print INDENT, book

# Söker på författare.
def search_author(catalog):
    author = raw_input('Vilken författare vill du söka efter? ')
    book_list = catalog.find_author(author)
    if len(book_list) == 0:
        print INDENT, 'Hittade inga böcker av den författaren.'
    else:
        print INDENT, 'Hittade', len(book_list), 'böcker'
        for book in book_list:
            print INDENT, book

# Lånar en bok.
def borrow_book(catalog):
    return

# Återlämnar en bok.
def return_book(catalog):
    return

# Lägger till en ny bok.
def add_book(catalog):
    return

# Tar bort en bok.
def delete_book(catalog):
    return


# --------  Huvudprogram  --------

print 'Välkommen till biblioteksprogrammet!'

FILENAME = 'bibliotek.txt'
catalog = Catalog(FILENAME)

# Testutskrift
print 'Antal böcker:', len(catalog.books)
for book in catalog.books:
    print book

print_menu()

choice = choose();
while choice != 'S':
    if choice == 'T':
        search_title(catalog)
    if choice == 'F':
        search_author(catalog)
    choice = choose()

catalog.save(FILENAME)

print INDENT, 'Välkommen åter!'


[redigera] Inlämningsuppgift: prototyp

Skriv en prototyp till din P-uppgift och lämna in den på vanligt sätt under rubriken "P-uppgift Prototyp".

Personliga verktyg