JavaSE基礎專案:改進版開發團隊人員排程軟體
前言
這個專案源自尚矽谷宋紅康老師的Java基礎課程,看上去很簡單,但是也困擾了我小半天。
一、專案需求
該專案實現以下功能
二、軟體架構設計
MVC三層架構
三、實現過程
第一步:建立專案基本元件
1、TSUtility工具類
package pers.victorgong.開發團隊的人員排程軟體.view;
import java.util.Scanner;
/**
* @Author: shkstart Email:shkstart@126.com
* @Description: 專案中提供了TSUtility.java類,可用來方便地實現鍵盤訪問。
* @Date: Created in 12:02 2019/02/12
*/
public class TSUtility {
private static Scanner scanner = new Scanner(System.in);
/**
*
* @Description 該方法讀取鍵盤,如果使用者鍵入’1’-’4’中的任意字元,則方法返回。返回值為使用者鍵入字元。
* @author shkstart
* @date 2019年2月12日上午12:03:30
* @return
*/
public static char readMenuSelection() {
char c;
for (; ; ) {
String str = readKeyBoard(1, false);
c = str.charAt(0);
if (c != '1' && c != '2' &&
c != '3' && c != '4') {
System.out.print("選擇錯誤,請重新輸入:");
} else break;
}
return c;
}
/**
*
* @Description 該方法提示並等待,直到使用者按Enter鍵後返回。
* @author shkstart
* @date 2019年2月12日上午12:03:50
*/
public static void readReturn() {
System.out.print("按Enter鍵繼續...");
readKeyBoard(100, true);
}
/**
*
* @Description 該方法從鍵盤讀取一個長度不超過2位的整數,並將其作為方法的返回值。
* @author shkstart
* @date 2019年2月12日上午12:04:04
* @return
*/
public static int readInt() {
int n;
for (; ; ) {
String str = readKeyBoard(2, false);
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.print("數字輸入錯誤,請重新輸入:");
}
}
return n;
}
/**
*
* @Description 從鍵盤讀取‘Y’或’N’,並將其作為方法的返回值。
* @author shkstart
* @date 2019年2月12日上午12:04:45
* @return
*/
public static char readConfirmSelection() {
char c;
for (; ; ) {
String str = readKeyBoard(1, false).toUpperCase();
c = str.charAt(0);
if (c == 'Y' || c == 'N') {
break;
} else {
System.out.print("選擇錯誤,請重新輸入:");
}
}
return c;
}
private static String readKeyBoard(int limit, boolean blankReturn) {
String line = "";
while (scanner.hasNextLine()) {
line = scanner.nextLine();
if (line.length() == 0) {
if (blankReturn) return line;
else continue;
}
if (line.length() < 1 || line.length() > limit) {
System.out.print("輸入長度(不大於" + limit + ")錯誤,請重新輸入:");
continue;
}
break;
}
return line;
}
}
2、Equipment介面
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description: 裝置類的介面
* @Date: Created in 15:01 2020/12/14
*/
public interface Equipment {
public String getDescription();
}
PC類
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description: 電腦
* @Date: Created in 15:07 2020/12/14
*/
public class PC implements Equipment{
private final String MODEL; //機器的型號
private final String DISPLAY; //顯示器型號
public PC(String MODEL, String DISPLAY) {
this.MODEL = MODEL;
this.DISPLAY = DISPLAY;
}
public String getMODEL() {
return this.MODEL;
}
public String getDISPLAY() {
return this.DISPLAY;
}
@Override
public String getDescription() {
return "PC{" +
"MODEL='" + MODEL + '\'' +
", DISPLAY='" + DISPLAY + '\'' +
'}';
}
}
NoteBook類
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description:
* @Date: Created in 15:12 2020/12/14
*/
public class NoteBook implements Equipment{
private final String MODEL;
private final int PRICE;
public NoteBook(String MODEL, int PRICE) {
this.MODEL = MODEL;
this.PRICE = PRICE;
}
public String getMODEL() {
return this.MODEL;
}
public int getPRICE() {
return this.PRICE;
}
@Override
public String getDescription() {
return "NoteBook{" +
"MODEL='" + MODEL + '\'' +
", PRICE=" + PRICE +
'}';
}
}
Printer類
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description: 印表機
* @Date: Created in 15:15 2020/12/14
*/
public class Printer implements Equipment{
private final String NAME;
private final String TYPE;
public Printer(String NAME, String TYPE) {
this.NAME = NAME;
this.TYPE = TYPE;
}
public String getNAME() {
return this.NAME;
}
public String getTYPE() {
return this.TYPE;
}
@Override
public String getDescription() {
return "Printer{" +
"NAME='" + NAME + '\'' +
", TYPE='" + TYPE + '\'' +
'}';
}
}
3、Employee類及其子類的設計
status類(service包中)
package pers.victorgong.開發團隊的人員排程軟體.service;
/**
* @Author: Victor Gong
* @Description:
* @Date: Created in 15:28 2020/12/14
*/
public class Status {
private final String NAME;
public Status(String name) {
this.NAME = name;
}
public static final Status FREE = new Status("FREE");
public static final Status VOCATION = new Status("VOCATION");
public static final Status BUSY = new Status("BUSY");
public String getNAME() {
return NAME;
}
@Override
public String toString() {
return NAME;
}
}
Employee類
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description: 員工
* @Date: Created in 15:02 2020/12/14
*/
public class Employee {
private final int ID;
private final String NAME;
private final int AGE;
private double salary;
private int memberId; //記錄成員加入開發團隊後在團隊中的ID
public Employee(int ID, String NAME, int AGE, double salary) {
this.ID = ID;
this.NAME = NAME;
this.AGE = AGE;
this.salary = salary;
}
public int getID() {
return ID;
}
public String getNAME() {
return NAME;
}
public int getAGE() {
return AGE;
}
public double getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public String getDetails() {
if (NAME.length() == 2) {
return ID + "\t\t" + NAME + "\t\t" + AGE + "\t\t" + salary;
}
return ID + "\t\t" + NAME + "\t" + AGE + "\t\t" + salary;
}
protected String getMemberDetails() {
return getMemberId() + "/" + getDetails();
}
public String getDetailsForTeam() {
return getMemberDetails() + "\t客服";
}
@Override
public String toString() {
return getDetails();
}
public int getMemberId() {
return memberId;
}
public void setMemberId(int memberId) {
this.memberId = memberId;
}
}
Programmer類
package pers.victorgong.開發團隊的人員排程軟體.domain;
import pers.victorgong.開發團隊的人員排程軟體.service.Status;
/**
* @Author: Victor Gong
* @Description: 程式設計師
* @Date: Created in 15:25 2020/12/14
*/
public class Programmer extends Employee{
private Status status; //員工狀態
private Equipment equipment;
public Programmer(int ID, String NAME, int AGE, double salary, Equipment equipment) {
super(ID, NAME, AGE, salary);
this.equipment = equipment;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public Equipment getEquipment() {
return equipment;
}
public void setEquipment(Equipment equipment) {
this.equipment = equipment;
}
protected String getMemberDetails() {
return getMemberId() + "/" + super.getDetails();
}
public String getDetailsForTeam() {
return getMemberDetails() + "\t程式設計師";
}
@Override
public String toString() {
return getDetails() + "\t程式設計師\t" + status + "\t\t\t\t\t" + equipment.getDescription() ;
}
}
Designer類
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description: 設計師
* @Date: Created in 15:38 2020/12/14
*/
public class Designer extends Programmer{
private double bonus; //獎金
public Designer(int ID, String NAME, int AGE, double salary, Equipment equipment, double bonus) {
super(ID, NAME, AGE, salary, equipment);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public String getDetailsForTeam() {
return getMemberDetails() + "\t設計師\t" + getBonus();
}
@Override
public String toString() {
return getDetails() + "\t設計師\t" + getStatus() + "\t" +
getBonus() +"\t\t\t" + getEquipment().getDescription();
}
}
Architect類(我這裡用SystemArchitect表示了)
package pers.victorgong.開發團隊的人員排程軟體.domain;
/**
* @Author: Victor Gong
* @Description: 架構師
* @Date: Created in 15:40 2020/12/14
*/
public class SystemArchitect extends Designer{
private int stock; //持有股份
public SystemArchitect(int ID, String NAME, int AGE, double salary, Equipment equipment, double bonus, int stock) {
super(ID, NAME, AGE, salary, equipment, bonus);
this.stock = stock;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public String getDetailsForTeam() {
return getMemberDetails() + "\t架構師\t" +
getBonus() + "\t" + getStock();
}
@Override
public String toString() {
return getDetails() + "\t架構師\t" + getStatus() + "\t" +
getBonus() + "\t" + getStock() + "\t" + getEquipment().getDescription();
}
}
第二步:實現service包中的類
1、NameListService類的設計
Data類(用於儲存資料)
package pers.victorgong.開發團隊的人員排程軟體.service;
/**
* @Author: Victor Gong
* @Description:
* @Date: Created in 14:53 2020/12/14
*/
public class Data {
public static final int EMPLOYEE = 10;
public static final int PROGRAMMER = 11;
public static final int DESIGNER = 12;
public static final int ARCHITECT = 13;
public static final int PC = 21;
public static final int NOTEBOOK = 22;
public static final int PRINTER = 23;
//Employee : 10, id, name, age, salary
//Programmer: 11, id, name, age, salary
//Designer : 12, id, name, age, salary, bonus
//Architect : 13, id, name, age, salary, bonus, stock
public static final String[][] EMPLOYEES = {
{"10", "1", "龐巨集毅", "22", "3000"},
{"13", "2", "關鋒", "32", "18000", "15000", "2000"},
{"11", "3", "法越彬", "23", "7000"},
{"11", "4", "高雲柱", "24", "7300"},
{"12", "5", "趙波", "28", "10000", "5000"},
{"11", "6", "龔俊良", "22", "6800"},
{"12", "7", "張暉", "29", "10800","5200"},
{"13", "8", "劉鈺", "30", "19800", "15000", "2500"},
{"12", "9", "諸葛裕", "26", "9800", "5500"},
{"11", "10", "黃曦", "21", "6600"},
{"11", "11", "馬齊", "25", "7100"},
{"12", "12", "魏成周", "27", "9600", "4800"}
};
//如下的EQUIPMENTS陣列與上面的EMPLOYEES陣列元素一一對應
//PC :21, model, display
//NoteBook:22, model, price
//Printer :23, name, type
public static final String[][] EQUIPMENTS = {
{},
{"22", "聯想T4", "6000"},
{"21", "戴爾", "NEC17寸"},
{"21", "戴爾", "三星 17寸"},
{"23", "佳能 2900", "鐳射"},
{"21", "華碩", "三星 17寸"},
{"21", "華碩", "三星 17寸"},
{"23", "愛普生20K", "針式"},
{"22", "惠普m6", "5800"},
{"21", "戴爾", "NEC 17寸"},
{"21", "華碩","三星 17寸"},
{"22", "惠普m6", "5800"}
};
}
NameListService類
在此類中設定了員工的status
package pers.victorgong.開發團隊的人員排程軟體.service;
import pers.victorgong.開發團隊的人員排程軟體.domain.*;
/**
* @Author: Victor Gong
* @Description:
* @Date: Created in 16:21 2020/12/14
*/
public class NameListService {
private final Employee[] employees;
public NameListService() {
employees = new Employee[12];
for (int i = 0; i < employees.length; i++) {
int id = Integer.parseInt(Data.EMPLOYEES[i][1]); //id號
String name = Data.EMPLOYEES[i][2]; //姓名
int age = Integer.parseInt(Data.EMPLOYEES[i][3]); //年齡
double salary = Double.parseDouble(Data.EMPLOYEES[i][4]); //工資
Equipment equipment = null;
if (i != 0) {
equipment = switch (Data.EQUIPMENTS[i][0]) { //裝置資訊
case "21" -> new PC(Data.EQUIPMENTS[i][1], Data.EQUIPMENTS[i][2]);
case "22" -> new NoteBook(Data.EQUIPMENTS[i][1], Integer.parseInt(Data.EQUIPMENTS[i][2]));
case "23" -> new Printer(Data.EQUIPMENTS[i][1], Data.EQUIPMENTS[i][2]);
default -> null;
};
}
if (Data.EMPLOYEES[i][0].equals("10")) { //普通員工
employees[i] = new Employee(id, name, age, salary);
}else if (Data.EMPLOYEES[i][0].equals("11")) {//程式設計師
Programmer p = new Programmer(id, name, age, salary, equipment);
Status status = new Status("FREE");
p.setStatus(status);
employees[i] = p;
}else if (Data.EMPLOYEES[i][0].equals("12")) { //設計師
Designer d = new Designer(id, name, age ,salary, equipment,
Double.parseDouble(Data.EMPLOYEES[i][5]));
Status status = new Status("FREE");
d.setStatus(status);
employees[i] = d;
}else if (Data.EMPLOYEES[i][0].equals("13")) { //架構師
SystemArchitect architect = new SystemArchitect(id, name, age, salary, equipment,
Double.parseDouble(Data.EMPLOYEES[i][5]), Integer.parseInt(Data.EMPLOYEES[i][6]));
Status status = new Status("FREE");
architect.setStatus(status);
employees[i] = architect;
}
}
}
/**
*獲取當前所有員工
* @return 包含所有員工物件的陣列
*/
public Employee[] getAllEmployees() {
return this.employees;
}
/**
*獲取指定ID的員工物件
* @param id 指定員工的ID
* @return 指定員工物件
*/
public Employee getEmployee(int id) throws TeamException{
return employees[id-1];
}
}
自定義異常類TeamException
package pers.victorgong.開發團隊的人員排程軟體.service;
/**
* @Author: Victor Gong
* @Description:
* @Date: Created in 17:11 2020/12/14
*/
public class TeamException extends Exception{
static final long serialVersionUID = -33875169124229948L;
public TeamException() {
}
public TeamException(String message) {
super(message);
}
}
2、TeamService類的設計
TeamService類:
我在此類中新增了一個輸入TID得到員工資訊的方法
package pers.victorgong.開發團隊的人員排程軟體.service;
import pers.victorgong.開發團隊的人員排程軟體.domain.Designer;
import pers.victorgong.開發團隊的人員排程軟體.domain.Employee;
import pers.victorgong.開發團隊的人員排程軟體.domain.Programmer;
import pers.victorgong.開發團隊的人員排程軟體.domain.SystemArchitect;
/**
* @Author: Victor Gong
* @Description: 關於開發團隊成員的管理:新增、刪除等
* @Date: Created in 17:38 2020/12/14
*/
public class TeamService {
private static int counter = 1; //團隊中員工的id
private final int MAX_MEMBER = 5; //最多成員數量
private Programmer[] members; //團隊成員
private int total; //團隊成員的實際人數
public TeamService() {
members = new Programmer[MAX_MEMBER];
total = 0;
}
/**
*
* @return 當前團隊的所有物件
*/
public Employee[] getTeam() {
Employee[] temp = new Employee[total];
for (int i = 0; i < total; i++) {
temp[i] = members[i];
}
return temp;
}
/**
* 向團隊中新增成員
* @param e 員工
* @throws TeamException
*/
public void addMember(Employee e) throws TeamException{
if (total >= MAX_MEMBER) {
throw new TeamException("成員已滿,無法新增");
}
if (!(e instanceof Programmer)) {
throw new TeamException("該成員不是開發人員,無法新增");
}
Programmer p = (Programmer) e;
if (isExist(p)) {
throw new TeamException("該員工已在本團隊中");
}
if(p.getStatus().getNAME().equals("BUSY")) {
throw new TeamException("該員工已是某團隊成員");
}else if(p.getStatus().getNAME().equals("VOCATION")) {
throw new TeamException("該員正在休假,無法新增");
}
int numOfArch = 0, numOfDsgn = 0, numOfPrg = 0;
for (int i = 0; i < total; i++) {
if (members[i] instanceof SystemArchitect) numOfArch++;
else if (members[i] instanceof Designer) numOfDsgn++;
else if (members[i] instanceof Programmer) numOfPrg++;
}
if (p instanceof SystemArchitect) {
if (numOfArch >= 1) throw new TeamException("團隊中至多隻能有一名架構師");
} else if (p instanceof Designer) {
if (numOfDsgn >= 2) throw new TeamException("團隊中至多隻能有兩名設計師");
} else if (p instanceof Programmer) {
if (numOfPrg >= 3) throw new TeamException("團隊中至多隻能有三名程式設計師");
}
p.setStatus(Status.BUSY);
p.setMemberId(counter++);
members[total++] = p;
}
/**
*
* @param e 員工
* @return 員工是否已經新增到團隊中
*/
private boolean isExist(Programmer p) {
for (int i = 0; i < total; i++) {
if (members[i].getID() == p.getID()) {
return true;
}
}
return false;
}
/**
*從團隊中刪除成員
* @param memberId 待刪除成員的memberId
* @throws TeamException
*/
public void removeMember(int memberId) throws TeamException{
int i;
for (i = 0; i < total; i++) {
if (members[i].getMemberId() == memberId) {
members[i].setStatus(Status.FREE);
break;
}
}
//如果遍歷一遍,都找不到,則報異常
if (i == total) {
throw new TeamException("找不到該成員,無法刪除");
}
//後面的元素覆蓋前面的元素
for (int j = i; j < total - 1; j++) {
members[j] = members[j+1];
}
members[--total] = null;
}
public int getMAX_MEMBER() {
return this.MAX_MEMBER;
}
public int getTotal() {
return this.total;
}
/**
*
* @param tId 團佇列表中的編號
* @return 員工資訊
*/
public Programmer getEmployee(int tId) throws TeamException{
return members[tId-1];
}
}
第三步:實現view包中類
1、TeamView類
我在此類的deleteMember()方法中稍作改進,使得在刪除之前還能顯示要刪除的這名員工的資訊
package pers.victorgong.開發團隊的人員排程軟體.view;
import pers.victorgong.開發團隊的人員排程軟體.domain.Employee;
import pers.victorgong.開發團隊的人員排程軟體.domain.Programmer;
import pers.victorgong.開發團隊的人員排程軟體.service.NameListService;
import pers.victorgong.開發團隊的人員排程軟體.service.TeamException;
import pers.victorgong.開發團隊的人員排程軟體.service.TeamService;
/**
* @Author: Victor Gong
* @Description:
* @Date: Created in 18:49 2020/12/14
*/
public class TeamView {
private final NameListService listSvc; //公司成員
private final TeamService teamSvc; //開發團隊成員
public TeamView() {
listSvc = new NameListService();
teamSvc = new TeamService();
}
/**
* 主介面顯示及控制方法
*/
public void enterMainMenu() {
char key = 0;
while (true) {
if (key != '1') {
listAllEmployees();
}
System.out.print("1-團佇列表 2-新增團隊成員 3-刪除團隊成員 4-退出 請選擇(1-4):");
key = TSUtility.readMenuSelection();
System.out.println();
switch (key) {
case '1' -> getTeam();
case '2' -> addMember();
case '3' -> deleteMember();
case '4' -> {
System.out.print("確認是否退出(Y/N):");
char yn = TSUtility.readConfirmSelection();
if (yn == 'Y') {
return;
}
}
}
}
}
/**
* 以表格形式列出公司所有成員
*/
private void listAllEmployees() {
System.out.println("-------------------------------------開發團隊排程軟體--------------------------------------");
Employee[] employees = listSvc.getAllEmployees();
if (employees.length == 0) {
System.out.println("沒有客戶記錄!");
}else {
System.out.println("ID\t\t姓名\t\t年齡\t\t工資\t\t職位\t\t狀態\t\t獎金\t\t股票\t\t領用裝置");
}
for (Employee e : employees) {
System.out.println(" " + e);
}
System.out.println("----------------------------------------------------------------------------------------------");
}
/**
* 顯示團隊成員列表操作
*/
private void getTeam() {
System.out.println("--------------------團隊成員列表---------------------");
Employee[] employees = teamSvc.getTeam();
if (teamSvc.getTotal() == 0) {
System.out.println("開發團隊目前沒有成員!");
}else {
System.out.println("TID/ID\t\t姓名\t\t年齡\t\t工資\t\t職位\t\t獎金\t\t股票");
for (Employee e : employees) {
System.out.println(" " + e.getDetailsForTeam());
}
}
System.out.println("-----------------------------------------------------");
}
/**
* 新增成員操作
*/
private void addMember() {
System.out.println("---------------------新增成員---------------------");
System.out.println("請輸入要新增的員工ID:");
int id = TSUtility.readInt();
try {
Employee e = listSvc.getEmployee(id);
teamSvc.addMember(e);
System.out.println("新增成功");
} catch (TeamException teamException) {
System.out.println("新增失敗,原因:" + teamException.getMessage());
}
// 按Enter鍵繼續...
TSUtility.readReturn();
}
/**
* 刪除成員操作
*/
private void deleteMember() {
System.out.println("---------------------刪除成員---------------------");
System.out.print("請輸入要刪除員工的TID:");
int id = TSUtility.readInt();
Programmer p;
try {
System.out.println("TID\t\t姓名\t\t年齡\t\t工資\t\t職位\t\t獎金\t\t股票");
p = teamSvc.getEmployee(id);
System.out.println(p.getDetailsForTeam());
} catch (TeamException teamException) {
System.out.println("刪除失敗,原因:" + teamException.getMessage());
}
System.out.print("確認是否刪除(Y/N):");
char yn = TSUtility.readConfirmSelection();
if (yn == 'N') {
return;
}
try {
teamSvc.removeMember(id);
System.out.println("刪除成功");
} catch (TeamException teamException) {
System.out.println("刪除失敗,原因:" + teamException.getMessage());
}
// 按Enter鍵繼續...
TSUtility.readReturn();
}
}
2、測試類
package pers.victorgong.開發團隊的人員排程軟體.view;
/**
* @Author: Victor Gong
* @Description: 測試
* @Date: Created in 18:49 2020/12/14
*/
public class ProjectTest {
public static void main(String[] args) {
TeamView teamView = new TeamView();
teamView.enterMainMenu();
}
}
四、測試結果
新增的員工非程式設計師
新增成功
員工已在團隊中
顯示團隊成員
刪除操作
五、總結
1.整個專案是看照著課件思路一個一個做的,中間遇到了很多難題,慢慢解決克服這些困難之後收穫非常大。
2.這個專案過後就可以暫時告別JavaSE基礎部分的學習,開始後半部分,希望未來的學習之路更加順利!
六、最後
感謝大家的閱讀,自學Java的朋友也可以跟我交流學習方面的內容哦。
如文章有誤還請糾正,謝謝
相關文章
- 【Java】基礎_14_開發團隊排程系統Java
- 分析如何使用專案管理軟體管理軟體開發團隊專案管理
- 傳統文化研究團隊------軟體工程團隊專案軟體工程
- QCon 全球軟體開發大會 | 大型團隊研發效率持續改進實踐
- 軟體專案過程診斷與改進建議案例
- 團隊專案管理軟體哪個好?專案管理
- [杭州] 阿里系統軟體事業部排程團隊 Java/Golang高階開發工程師阿里JavaGolang工程師
- 禪道專案管理軟體,敏捷開發團隊不可或缺的工具專案管理敏捷
- 比較專案計劃軟體或專案排程軟體哪個好用?
- 迭代結束,專案經理要求測試人員發郵件,如何與團隊成員分擔發版的權利?
- 專案管理軟體排程的優勢有哪些?專案管理
- 谷歌員工爆料Python基礎團隊原地解散谷歌Python
- 探究如何管理和領導遠端開發人員團隊
- 運營人員使用什麼專案管理軟體?專案管理
- 軟體工程課程專案“物品復活“軟體開發v1.0軟體工程
- 白鯨開源中標人保集團資料排程工具軟體產品及服務採購專案!
- 為什麼說減少開發人員和安全團隊之間摩擦有助提高軟體安全性
- 實現快速與團隊成員進行檔案共享,提高團隊辦公效率
- 團隊專案一
- 軟體研發之道:微軟開發團隊的經驗法則微軟
- IT專案開發團隊建設與管理總結(轉)
- 如何使用ABP進行軟體開發之基礎概覽
- 如何成為更好的軟體開發人員
- 敏捷開發專案管理軟體敏捷專案管理
- 團隊管理、團隊人員技術培養 的 思考和交流
- 公排開發原始碼版丨公排系統開發(技術方案)丨公排系統開發(開發專案)原始碼
- 五款高效易用的專案管理軟體,提升團隊工作效率專案管理
- Wiki憑什麼持續得到開發人員和團隊的喜愛
- 2010.03.16專題:一個開發人員的專案煩惱
- 盤點8款日程為基礎的團隊協作軟體推薦
- 軟體專案管理 9.2.軟體專案配置管理過程專案管理
- 如何利用六西格瑪有效管理專案團隊成員?
- 81%的開發人員表示知道軟體存在缺陷
- 零基礎ASP.NET Core WebAPI團隊協作開發ASP.NETWebAPI
- 邊緣計算正在改變IT專業人員對基礎設施的思考方式
- 團隊專案4——專案衝刺-4
- 團隊專案4——專案衝刺-3
- 【專題1:電子工程師 之 軟體】 之 【15.軟體開發流程(b)- 人員協作】工程師